aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/feebumper.cpp1
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/psbtwallet.h2
-rw-r--r--src/wallet/rpcdump.cpp26
-rw-r--r--src/wallet/rpcwallet.cpp753
-rw-r--r--src/wallet/scriptpubkeyman.cpp24
-rw-r--r--src/wallet/test/coinselector_tests.cpp13
-rw-r--r--src/wallet/test/scriptpubkeyman_tests.cpp43
-rw-r--r--src/wallet/wallet.cpp23
-rw-r--r--src/wallet/wallet.h4
10 files changed, 506 insertions, 385 deletions
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index b93b9ef1bc..486a6b8bf1 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -12,7 +12,6 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/system.h>
-#include <util/validation.h>
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index 2ebc9aba39..50f064b305 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -62,7 +62,7 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
- gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes. %s in cmd is replaced by TxID and %w is replaced by wallet name. %w is not currently implemented on windows. On systems where %w is supported, it should NOT be quoted because this would break shell escaping used to invoke the command.", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup"
diff --git a/src/wallet/psbtwallet.h b/src/wallet/psbtwallet.h
index b35a0a58d1..8285bb901c 100644
--- a/src/wallet/psbtwallet.h
+++ b/src/wallet/psbtwallet.h
@@ -27,6 +27,6 @@ NODISCARD TransactionError FillPSBT(const CWallet* pwallet,
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
- bool bip32derivs = false);
+ bool bip32derivs = true);
#endif // BITCOIN_WALLET_PSBTWALLET_H
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index b730d4a4dd..4253038090 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -688,7 +688,7 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for the private key"},
},
RPCResult{
- "\"key\" (string) The private key\n"
+ RPCResult::Type::STR, "key", "The private key"
},
RPCExamples{
HelpExampleCli("dumpprivkey", "\"myaddress\"")
@@ -738,9 +738,10 @@ UniValue dumpwallet(const JSONRPCRequest& request)
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The filename with path (either absolute or relative to bitcoind)"},
},
RPCResult{
- "{ (json object)\n"
- " \"filename\" : { (string) The filename with full absolute path\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "filename", "The filename with full absolute path"},
+ }
},
RPCExamples{
HelpExampleCli("dumpwallet", "\"test\"")
@@ -1321,8 +1322,21 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
"\"options\""},
},
RPCResult{
- "\nResponse is an array with the same size as the input that has the execution result :\n"
- " [{\"success\": true}, {\"success\": true, \"warnings\": [\"Ignoring irrelevant private key\"]}, {\"success\": false, \"error\": {\"code\": -1, \"message\": \"Internal Server Error\"}}, ...]\n"
+ RPCResult::Type::ARR, "", "Response is an array with the same size as the input that has the execution result",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "success", ""},
+ {RPCResult::Type::ARR, "warnings", /* optional */ true, "",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
+ {RPCResult::Type::OBJ, "error", /* optional */ true, "",
+ {
+ {RPCResult::Type::ELISION, "", "JSONRPC error"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("importmulti", "'[{ \"scriptPubKey\": { \"address\": \"<my address>\" }, \"timestamp\":1455191478 }, "
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index df71f97e85..d9d92f8984 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -19,11 +19,12 @@
#include <script/sign.h>
#include <util/bip32.h>
#include <util/fees.h>
+#include <util/message.h> // For MessageSign()
#include <util/moneystr.h>
#include <util/string.h>
#include <util/system.h>
#include <util/url.h>
-#include <util/validation.h>
+#include <util/vector.h>
#include <wallet/coincontrol.h>
#include <wallet/feebumper.h>
#include <wallet/psbtwallet.h>
@@ -205,7 +206,7 @@ static UniValue getnewaddress(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "\"address\" (string) The new bitcoin address\n"
+ RPCResult::Type::STR, "address", "The new bitcoin address"
},
RPCExamples{
HelpExampleCli("getnewaddress", "")
@@ -256,7 +257,7 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "\"address\" (string) The address\n"
+ RPCResult::Type::STR, "address", "The address"
},
RPCExamples{
HelpExampleCli("getrawchangeaddress", "")
@@ -390,7 +391,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
" dirty if they have previously been used in a transaction."},
},
RPCResult{
- "\"txid\" (string) The transaction id.\n"
+ RPCResult::Type::STR_HEX, "txid", "The transaction id."
},
RPCExamples{
HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
@@ -469,17 +470,18 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
"in past transactions\n",
{},
RPCResult{
- "[\n"
- " [\n"
- " [\n"
- " \"address\", (string) The bitcoin address\n"
- " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n"
- " \"label\" (string, optional) The label\n"
- " ]\n"
- " ,...\n"
- " ]\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The bitcoin address"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR, "label", /* optional */ true, "The label"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listaddressgroupings", "")
@@ -532,7 +534,7 @@ static UniValue signmessage(const JSONRPCRequest& request)
{"message", RPCArg::Type::STR, RPCArg::Optional::NO, "The message to create a signature of."},
},
RPCResult{
- "\"signature\" (string) The signature of the message encoded in base 64\n"
+ RPCResult::Type::STR, "signature", "The signature of the message encoded in base 64"
},
RPCExamples{
"\nUnlock the wallet for 30 seconds\n"
@@ -576,15 +578,13 @@ static UniValue signmessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
- CHashWriter ss(SER_GETHASH, 0);
- ss << strMessageMagic;
- ss << strMessage;
+ std::string signature;
- std::vector<unsigned char> vchSig;
- if (!key.SignCompact(ss.GetHash(), vchSig))
+ if (!MessageSign(key, strMessage, signature)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
+ }
- return EncodeBase64(vchSig.data(), vchSig.size());
+ return signature;
}
static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
@@ -603,7 +603,7 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
{"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
},
RPCResult{
- "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received at this address."
},
RPCExamples{
"\nThe amount from transactions with at least 1 confirmation\n"
@@ -673,7 +673,7 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
{"minconf", RPCArg::Type::NUM, /* default */ "1", "Only include transactions confirmed at least this many times."},
},
RPCResult{
- "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this label.\n"
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this label."
},
RPCExamples{
"\nAmount received by the default label with at least 1 confirmation\n"
@@ -745,7 +745,7 @@ static UniValue getbalance(const JSONRPCRequest& request)
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Do not include balance in dirty outputs; addresses are considered dirty if they have previously been used in a transaction."},
},
RPCResult{
- "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this wallet.\n"
+ RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received for this wallet."
},
RPCExamples{
"\nThe total amount in the wallet with 1 or more confirmations\n"
@@ -847,8 +847,8 @@ static UniValue sendmany(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
RPCResult{
- "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
- " the number of addresses.\n"
+ RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
+ "the number of addresses."
},
RPCExamples{
"\nSend two amounts to two different addresses:\n"
@@ -971,10 +971,12 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
{"address_type", RPCArg::Type::STR, /* default */ "set by -addresstype", "The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
},
RPCResult{
- "{\n"
- " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n"
- " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The value of the new multisig address"},
+ {RPCResult::Type::STR_HEX, "redeemScript", "The string value of the hex-encoded redemption script"},
+ {RPCResult::Type::STR, "descriptor", "The descriptor for this multisig"},
+ }
},
RPCExamples{
"\nAdd a multisig address from 2 addresses\n"
@@ -1018,9 +1020,13 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
CTxDestination dest = AddAndGetMultisigDestination(required, pubkeys, output_type, spk_man, inner);
pwallet->SetAddressBook(dest, label, "send");
+ // Make the descriptor
+ std::unique_ptr<Descriptor> descriptor = InferDescriptor(GetScriptForDestination(dest), spk_man);
+
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
+ result.pushKV("descriptor", descriptor->ToString());
return result;
}
@@ -1199,20 +1205,21 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request)
{"address_filter", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "If present, only return information on this address."},
},
RPCResult{
- "[\n"
- " {\n"
- " \"involvesWatchonly\" : true, (bool) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\" : \"receivingaddress\", (string) The receiving address\n"
- " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n"
- " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
- " \"label\" : \"label\", (string) The label of the receiving address. The default label is \"\".\n"
- " \"txids\": [\n"
- " \"txid\", (string) The ids of transactions received with the address \n"
- " ...\n"
- " ]\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::STR, "address", "The receiving address"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount in " + CURRENCY_UNIT + " received by the address"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
+ {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
+ {RPCResult::Type::ARR, "txids", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The ids of transactions received with the address"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listreceivedbyaddress", "")
@@ -1249,15 +1256,16 @@ static UniValue listreceivedbylabel(const JSONRPCRequest& request)
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Whether to include watch-only addresses (see 'importaddress')"},
},
RPCResult{
- "[\n"
- " {\n"
- " \"involvesWatchonly\" : true, (bool) Only returns true if imported addresses were involved in transaction.\n"
- " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this label\n"
- " \"confirmations\" : n, (numeric) The number of confirmations of the most recent transaction included\n"
- " \"label\" : \"label\" (string) The label of the receiving address. The default label is \"\".\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The total amount received by addresses with this label"},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations of the most recent transaction included"},
+ {RPCResult::Type::STR, "label", "The label of the receiving address. The default label is \"\""},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listreceivedbylabel", "")
@@ -1369,26 +1377,26 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, CWallet* con
}
}
-static const std::string TransactionDescriptionString()
+static const std::vector<RPCResult> TransactionDescriptionString()
{
- return " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Negative confirmations means the\n"
- " transaction conflicted that many blocks ago.\n"
- " \"generated\": xxx, (bool) Only present if transaction only input is a coinbase one.\n"
- " \"trusted\": xxx, (bool) Only present if we consider transaction to be trusted and so safe to spend from.\n"
- " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction.\n"
- " \"blockheight\": n, (numeric) The block height containing the transaction.\n"
- " \"blockindex\": n, (numeric) The index of the transaction in the block that includes it.\n"
- " \"blocktime\": xxx, (numeric) The block time expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"txid\": \"transactionid\", (string) The transaction id.\n"
- " \"walletconflicts\": [ (array) Conflicting transaction ids.\n"
- " \"txid\", (string) The transaction id.\n"
- " ...\n"
- " ],\n"
- " \"time\": xxx, (numeric) The transaction time expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"timereceived\": xxx, (numeric) The time received expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"comment\": \"...\", (string) If a comment is associated with the transaction, only present if not empty.\n"
- " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
- " may be unknown for unconfirmed transactions not in the mempool\n";
+ return{{RPCResult::Type::NUM, "confirmations", "The number of confirmations for the transaction. Negative confirmations means the\n"
+ "transaction conflicted that many blocks ago."},
+ {RPCResult::Type::BOOL, "generated", "Only present if transaction only input is a coinbase one."},
+ {RPCResult::Type::BOOL, "trusted", "Only present if we consider transaction to be trusted and so safe to spend from."},
+ {RPCResult::Type::STR_HEX, "blockhash", "The block hash containing the transaction."},
+ {RPCResult::Type::NUM, "blockheight", "The block height containing the transaction."},
+ {RPCResult::Type::NUM, "blockindex", "The index of the transaction in the block that includes it."},
+ {RPCResult::Type::NUM_TIME, "blocktime", "The block time expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ {RPCResult::Type::ARR, "walletconflicts", "Conflicting transaction ids.",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id."},
+ }},
+ {RPCResult::Type::NUM_TIME, "time", "The transaction time expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::NUM_TIME, "timereceived", "The time received expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::STR, "comment", "If a comment is associated with the transaction, only present if not empty."},
+ {RPCResult::Type::STR, "bip125-replaceable", "(\"yes|no|unknown\") Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
+ "may be unknown for unconfirmed transactions not in the mempool"}};
}
UniValue listtransactions(const JSONRPCRequest& request)
@@ -1411,27 +1419,31 @@ UniValue listtransactions(const JSONRPCRequest& request)
{"include_watchonly", RPCArg::Type::BOOL, /* default */ "true for watch-only wallets, otherwise false", "Include transactions to watch-only addresses (see 'importaddress')"},
},
RPCResult{
- "[\n"
- " {\n"
- " \"involvesWatchonly\": xxx, (bool) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\":\"address\", (string) The bitcoin address of the transaction.\n"
- " \"category\": (string) The transaction category.\n"
- " \"send\" Transactions sent.\n"
- " \"receive\" Non-coinbase transactions received.\n"
- " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- " \"orphan\" Orphaned coinbase transactions received.\n"
- " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
- " for all other categories\n"
- " \"label\": \"label\", (string) A comment for the address/transaction, if any\n"
- " \"vout\": n, (numeric) the vout value\n"
- " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
- " 'send' category of transactions.\n"
- + TransactionDescriptionString()
- + " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- " 'send' category of transactions.\n"
- " }\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ "for all other categories"},
+ {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ TransactionDescriptionString()),
+ {
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ "'send' category of transactions."},
+ })},
+ }
},
RPCExamples{
"\nList the most recent 10 transactions in the systems\n"
@@ -1495,23 +1507,10 @@ UniValue listtransactions(const JSONRPCRequest& request)
if ((nFrom + nCount) > (int)ret.size())
nCount = ret.size() - nFrom;
- std::vector<UniValue> arrTmp = ret.getValues();
-
- std::vector<UniValue>::iterator first = arrTmp.begin();
- std::advance(first, nFrom);
- std::vector<UniValue>::iterator last = arrTmp.begin();
- std::advance(last, nFrom+nCount);
-
- if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
- if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
-
- std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
-
- ret.clear();
- ret.setArray();
- ret.push_backV(arrTmp);
-
- return ret;
+ const std::vector<UniValue>& txs = ret.getValues();
+ UniValue result{UniValue::VARR};
+ result.push_backV({ txs.rend() - nFrom - nCount, txs.rend() - nFrom }); // Return oldest to newest
+ return result;
}
static UniValue listsinceblock(const JSONRPCRequest& request)
@@ -1535,32 +1534,39 @@ static UniValue listsinceblock(const JSONRPCRequest& request)
" (not guaranteed to work on pruned nodes)"},
},
RPCResult{
- "{\n"
- " \"transactions\": [\n"
- " \"involvesWatchonly\": xxx, (bool) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\":\"address\", (string) The bitcoin address of the transaction.\n"
- " \"category\": (string) The transaction category.\n"
- " \"send\" Transactions sent.\n"
- " \"receive\" Non-coinbase transactions received.\n"
- " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- " \"orphan\" Orphaned coinbase transactions received.\n"
- " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
- " for all other categories\n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n"
- + TransactionDescriptionString()
- + " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the 'send' category of transactions.\n"
- " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
- " \"label\" : \"label\" (string) A comment for the address/transaction, if any\n"
- " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n"
- " ],\n"
- " \"removed\": [\n"
- " <structure is the same as \"transactions\" above, only present if include_removed=true>\n"
- " Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count.\n"
- " ],\n"
- " \"lastblock\": \"lastblockhash\" (string) The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "transactions", "",
+ {
+ {RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", "The bitcoin address of the transaction."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and is positive\n"
+ "for all other categories"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ TransactionDescriptionString()),
+ {
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ "'send' category of transactions."},
+ {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::STR, "to", "If a comment to is associated with the transaction."},
+ })},
+ }},
+ {RPCResult::Type::ARR, "removed", "<structure is the same as \"transactions\" above, only present if include_removed=true>\n"
+ "Note: transactions that were re-added in the active chain will appear as-is in this array, and may thus have a positive confirmation count."
+ , {{RPCResult::Type::ELISION, "", ""},}},
+ {RPCResult::Type::STR_HEX, "lastblock", "The hash of the block (target_confirmations-1) from the best block on the main chain. This is typically used to feed back into listsinceblock the next time you call it. So you would generally use a target_confirmations of say 6, so you will be continually re-notified of transactions until they've reached 6 confirmations plus any new ones"},
+ }
},
RPCExamples{
HelpExampleCli("listsinceblock", "")
@@ -1668,35 +1674,41 @@ static UniValue gettransaction(const JSONRPCRequest& request)
"Whether to include a `decoded` field containing the decoded transaction (equivalent to RPC decoderawtransaction)"},
},
RPCResult{
- "{\n"
- " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n"
- " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
- " 'send' category of transactions.\n"
- + TransactionDescriptionString()
- + " \"details\" : [\n"
- " {\n"
- " \"involvesWatchonly\": xxx, (bool) Only returns true if imported addresses were involved in transaction.\n"
- " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n"
- " \"category\" : (string) The transaction category.\n"
- " \"send\" Transactions sent.\n"
- " \"receive\" Non-coinbase transactions received.\n"
- " \"generate\" Coinbase transactions received with more than 100 confirmations.\n"
- " \"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
- " \"orphan\" Orphaned coinbase transactions received.\n"
- " \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n"
- " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
- " 'send' category of transactions.\n"
- " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
- " 'send' category of transactions.\n"
- " }\n"
- " ,...\n"
- " ],\n"
- " \"hex\" : \"data\" (string) Raw data for transaction\n"
- " \"decoded\" : transaction (json object) Optional, the decoded transaction (only present when `verbose` is passed), equivalent to the\n"
- " RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "", Cat(Cat<std::vector<RPCResult>>(
+ {
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the\n"
+ "'send' category of transactions."},
+ },
+ TransactionDescriptionString()),
+ {
+ {RPCResult::Type::ARR, "details", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::BOOL, "involvesWatchonly", "Only returns true if imported addresses were involved in transaction."},
+ {RPCResult::Type::STR, "address", "The bitcoin address involved in the transaction."},
+ {RPCResult::Type::STR, "category", "The transaction category.\n"
+ "\"send\" Transactions sent.\n"
+ "\"receive\" Non-coinbase transactions received.\n"
+ "\"generate\" Coinbase transactions received with more than 100 confirmations.\n"
+ "\"immature\" Coinbase transactions received with 100 or fewer confirmations.\n"
+ "\"orphan\" Orphaned coinbase transactions received."},
+ {RPCResult::Type::STR_AMOUNT, "amount", "The amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::STR, "label", "A comment for the address/transaction, if any"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n"
+ "'send' category of transactions."},
+ {RPCResult::Type::BOOL, "abandoned", "'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ "'send' category of transactions."},
+ }},
+ }},
+ {RPCResult::Type::STR_HEX, "hex", "Raw data for transaction"},
+ {RPCResult::Type::OBJ, "decoded", "Optional, the decoded transaction (only present when `verbose` is passed)",
+ {
+ {RPCResult::Type::ELISION, "", "Equivalent to the RPC decoderawtransaction method, or the RPC getrawtransaction method when `verbose` is passed."},
+ }},
+ })
},
RPCExamples{
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"")
@@ -2155,7 +2167,7 @@ static UniValue lockunspent(const JSONRPCRequest& request)
},
},
RPCResult{
- "true|false (boolean) Whether the command was successful or not\n"
+ RPCResult::Type::BOOL, "", "Whether the command was successful or not"
},
RPCExamples{
"\nList the unspent transactions\n"
@@ -2265,13 +2277,14 @@ static UniValue listlockunspent(const JSONRPCRequest& request)
"See the lockunspent call to lock and unlock transactions for spending.\n",
{},
RPCResult{
- "[\n"
- " {\n"
- " \"txid\" : \"transactionid\", (string) The transaction id locked\n"
- " \"vout\" : n (numeric) The vout value\n"
- " }\n"
- " ,...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The transaction id locked"},
+ {RPCResult::Type::NUM, "vout", "The vout value"},
+ }},
+ }
},
RPCExamples{
"\nList the unspent transactions\n"
@@ -2321,7 +2334,7 @@ static UniValue settxfee(const JSONRPCRequest& request)
{"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kB"},
},
RPCResult{
- "true|false (boolean) Returns true if successful\n"
+ RPCResult::Type::BOOL, "", "Returns true if successful"
},
RPCExamples{
HelpExampleCli("settxfee", "0.00001")
@@ -2359,19 +2372,23 @@ static UniValue getbalances(const JSONRPCRequest& request)
"Returns an object with all balances in " + CURRENCY_UNIT + ".\n",
{},
RPCResult{
- "{\n"
- " \"mine\": { (object) balances from outputs that the wallet can sign\n"
- " \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
- " \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
- " \"immature\": xxx (numeric) balance from immature coinbase outputs\n"
- " \"used\": xxx (numeric) (only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)\n"
- " },\n"
- " \"watchonly\": { (object) watchonly balances (not present if wallet does not watch anything)\n"
- " \"trusted\": xxx (numeric) trusted balance (outputs created by the wallet or confirmed outputs)\n"
- " \"untrusted_pending\": xxx (numeric) untrusted pending balance (outputs created by others that are in the mempool)\n"
- " \"immature\": xxx (numeric) balance from immature coinbase outputs\n"
- " },\n"
- "}\n"},
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::OBJ, "mine", "balances from outputs that the wallet can sign",
+ {
+ {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
+ {RPCResult::Type::STR_AMOUNT, "used", "(only present if avoid_reuse is set) balance from coins sent to addresses that were previously spent from (potentially privacy violating)"},
+ }},
+ {RPCResult::Type::OBJ, "watchonly", "watchonly balances (not present if wallet does not watch anything)",
+ {
+ {RPCResult::Type::STR_AMOUNT, "trusted", "trusted balance (outputs created by the wallet or confirmed outputs)"},
+ {RPCResult::Type::STR_AMOUNT, "untrusted_pending", "untrusted pending balance (outputs created by others that are in the mempool)"},
+ {RPCResult::Type::STR_AMOUNT, "immature", "balance from immature coinbase outputs"},
+ }},
+ }
+ },
RPCExamples{
HelpExampleCli("getbalances", "") +
HelpExampleRpc("getbalances", "")},
@@ -2425,27 +2442,29 @@ static UniValue getwalletinfo(const JSONRPCRequest& request)
"Returns an object containing various wallet state info.\n",
{},
RPCResult{
- "{\n"
- " \"walletname\": xxxxx, (string) the wallet name\n"
- " \"walletversion\": xxxxx, (numeric) the wallet version\n"
- " \"balance\": xxxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.trusted\n"
- " \"unconfirmed_balance\": xxx, (numeric) DEPRECATED. Identical to getbalances().mine.untrusted_pending\n"
- " \"immature_balance\": xxxxxx, (numeric) DEPRECATED. Identical to getbalances().mine.immature\n"
- " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n"
- " \"keypoololdest\": xxxxxx, (numeric) the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool\n"
- " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated (only counts external keys)\n"
- " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n"
- " \"unlocked_until\": ttt, (numeric) the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked\n"
- " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n"
- " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n"
- " \"private_keys_enabled\": true|false (boolean) false if privatekeys are disabled for this wallet (enforced watch-only wallet)\n"
- " \"avoid_reuse\": true|false (boolean) whether this wallet tracks clean/dirty coins in terms of reuse\n"
- " \"scanning\": (json object) current scanning details, or false if no scan is in progress\n"
- " {\n"
- " \"duration\" : xxxx (numeric) elapsed seconds since scan start\n"
- " \"progress\" : x.xxxx, (numeric) scanning progress percentage [0.0, 1.0]\n"
- " }\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {
+ {RPCResult::Type::STR, "walletname", "the wallet name"},
+ {RPCResult::Type::NUM, "walletversion", "the wallet version"},
+ {RPCResult::Type::STR_AMOUNT, "balance", "DEPRECATED. Identical to getbalances().mine.trusted"},
+ {RPCResult::Type::STR_AMOUNT, "unconfirmed_balance", "DEPRECATED. Identical to getbalances().mine.untrusted_pending"},
+ {RPCResult::Type::STR_AMOUNT, "immature_balance", "DEPRECATED. Identical to getbalances().mine.immature"},
+ {RPCResult::Type::NUM, "txcount", "the total number of transactions in the wallet"},
+ {RPCResult::Type::NUM_TIME, "keypoololdest", "the " + UNIX_EPOCH_TIME + " of the oldest pre-generated key in the key pool"},
+ {RPCResult::Type::NUM, "keypoolsize", "how many new keys are pre-generated (only counts external keys)"},
+ {RPCResult::Type::NUM, "keypoolsize_hd_internal", "how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)"},
+ {RPCResult::Type::NUM_TIME, "unlocked_until", "the " + UNIX_EPOCH_TIME + " until which the wallet is unlocked for transfers, or 0 if the wallet is locked"},
+ {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"},
+ {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "the Hash160 of the HD seed (only present when HD is enabled)"},
+ {RPCResult::Type::BOOL, "private_keys_enabled", "false if privatekeys are disabled for this wallet (enforced watch-only wallet)"},
+ {RPCResult::Type::BOOL, "avoid_reuse", "whether this wallet tracks clean/dirty coins in terms of reuse"},
+ {RPCResult::Type::OBJ, "scanning", "current scanning details, or false if no scan is in progress",
+ {
+ {RPCResult::Type::NUM, "duration", "elapsed seconds since scan start"},
+ {RPCResult::Type::NUM, "progress", "scanning progress percentage [0.0, 1.0]"},
+ }},
+ }},
},
RPCExamples{
HelpExampleCli("getwalletinfo", "")
@@ -2507,14 +2526,16 @@ static UniValue listwalletdir(const JSONRPCRequest& request)
"Returns a list of wallets in the wallet directory.\n",
{},
RPCResult{
- "{\n"
- " \"wallets\" : [ (json array of objects)\n"
- " {\n"
- " \"name\" : \"name\" (string) The wallet name\n"
- " }\n"
- " ,...\n"
- " ]\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ARR, "wallets", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "The wallet name"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listwalletdir", "")
@@ -2541,10 +2562,10 @@ static UniValue listwallets(const JSONRPCRequest& request)
"For full information on the wallet, use \"getwalletinfo\"\n",
{},
RPCResult{
- "[ (json array of strings)\n"
- " \"walletname\" (string) the wallet name\n"
- " ...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "walletname", "the wallet name"},
+ }
},
RPCExamples{
HelpExampleCli("listwallets", "")
@@ -2577,10 +2598,11 @@ static UniValue loadwallet(const JSONRPCRequest& request)
{"filename", RPCArg::Type::STR, RPCArg::Optional::NO, "The wallet directory or .dat file."},
},
RPCResult{
- "{\n"
- " \"name\" : <wallet_name>, (string) The wallet name if loaded successfully.\n"
- " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "The wallet name if loaded successfully."},
+ {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ }
},
RPCExamples{
HelpExampleCli("loadwallet", "\"test.dat\"")
@@ -2632,11 +2654,12 @@ static UniValue setwalletflag(const JSONRPCRequest& request)
{"value", RPCArg::Type::BOOL, /* default */ "true", "The new state."},
},
RPCResult{
- "{\n"
- " \"flag_name\": string (string) The name of the flag that was modified\n"
- " \"flag_state\": bool (bool) The new state of the flag\n"
- " \"warnings\": string (string) Any warnings associated with the change\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "flag_name", "The name of the flag that was modified"},
+ {RPCResult::Type::BOOL, "flag_state", "The new state of the flag"},
+ {RPCResult::Type::STR, "warnings", "Any warnings associated with the change"},
+ }
},
RPCExamples{
HelpExampleCli("setwalletflag", "avoid_reuse")
@@ -2692,10 +2715,11 @@ static UniValue createwallet(const JSONRPCRequest& request)
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "false", "Keep track of coin reuse, and treat dirty and clean coins differently with privacy considerations in mind."},
},
RPCResult{
- "{\n"
- " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n"
- " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "name", "The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path."},
+ {RPCResult::Type::STR, "warning", "Warning message if wallet was not loaded cleanly."},
+ }
},
RPCExamples{
HelpExampleCli("createwallet", "\"testwallet\"")
@@ -2821,27 +2845,28 @@ static UniValue listunspent(const JSONRPCRequest& request)
"query_options"},
},
RPCResult{
- "[ (array of json object)\n"
- " {\n"
- " \"txid\" : \"txid\", (string) the transaction id \n"
- " \"vout\" : n, (numeric) the vout value\n"
- " \"address\" : \"address\", (string) the bitcoin address\n"
- " \"label\" : \"label\", (string) The associated label, or \"\" for the default label\n"
- " \"scriptPubKey\" : \"key\", (string) the script key\n"
- " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
- " \"confirmations\" : n, (numeric) The number of confirmations\n"
- " \"redeemScript\" : \"script\" (string) The redeemScript if scriptPubKey is P2SH\n"
- " \"witnessScript\" : \"script\" (string) witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH\n"
- " \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
- " \"solvable\" : xxx, (bool) Whether we know how to spend this output, ignoring the lack of keys\n"
- " \"reused\" : xxx, (bool) (only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)\n"
- " \"desc\" : xxx, (string, only when solvable) A descriptor for spending this output\n"
- " \"safe\" : xxx (bool) Whether this output is considered safe to spend. Unconfirmed transactions\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "the transaction id"},
+ {RPCResult::Type::NUM, "vout", "the vout value"},
+ {RPCResult::Type::STR, "address", "the bitcoin address"},
+ {RPCResult::Type::STR, "label", "The associated label, or \"\" for the default label"},
+ {RPCResult::Type::STR, "scriptPubKey", "the script key"},
+ {RPCResult::Type::STR_AMOUNT, "amount", "the transaction output amount in " + CURRENCY_UNIT},
+ {RPCResult::Type::NUM, "confirmations", "The number of confirmations"},
+ {RPCResult::Type::STR_HEX, "redeemScript", "The redeemScript if scriptPubKey is P2SH"},
+ {RPCResult::Type::STR, "witnessScript", "witnessScript if the scriptPubKey is P2WSH or P2SH-P2WSH"},
+ {RPCResult::Type::BOOL, "spendable", "Whether we have the private keys to spend this output"},
+ {RPCResult::Type::BOOL, "solvable", "Whether we know how to spend this output, ignoring the lack of keys"},
+ {RPCResult::Type::BOOL, "reused", "(only present if avoid_reuse is set) Whether this output is reused/dirty (sent to an address that was previously spent from)"},
+ {RPCResult::Type::STR, "desc", "(only when solvable) A descriptor for spending this output"},
+ {RPCResult::Type::BOOL, "safe", "Whether this output is considered safe to spend. Unconfirmed transactions"
" from outside keys and unconfirmed replacement transactions are considered unsafe\n"
- " and are not eligible for spending by fundrawtransaction and sendtoaddress.\n"
- " }\n"
- " ,...\n"
- "]\n"
+ "and are not eligible for spending by fundrawtransaction and sendtoaddress."},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("listunspent", "")
@@ -2931,7 +2956,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
CTxDestination address;
const CScript& scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
- bool reused = avoid_reuse && pwallet->IsUsedDestination(out.tx->GetHash(), out.i);
+ bool reused = avoid_reuse && pwallet->IsSpentKey(out.tx->GetHash(), out.i);
if (destinations.size() && (!fValidAddress || !destinations.count(address)))
continue;
@@ -3179,11 +3204,12 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
},
RPCResult{
- "{\n"
- " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
- " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
- " \"changepos\": n (numeric) The position of the added change output, or -1\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "The resulting raw transaction (hex-encoded string)"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
+ {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
+ }
},
RPCExamples{
"\nCreate a transaction with no inputs\n"
@@ -3258,20 +3284,22 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
" \"SINGLE|ANYONECANPAY\""},
},
RPCResult{
- "{\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"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "hex", "The hex-encoded raw transaction with signature(s)"},
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ {RPCResult::Type::ARR, "errors", "Script verification errors (if there are any)",
+ {
+ {RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR_HEX, "txid", "The hash of the referenced, previous transaction"},
+ {RPCResult::Type::NUM, "vout", "The index of the output to spent and used as input"},
+ {RPCResult::Type::STR_HEX, "scriptSig", "The hex-encoded signature script"},
+ {RPCResult::Type::NUM, "sequence", "Script sequence number"},
+ {RPCResult::Type::STR, "error", "Verification or signing error related to the input"},
+ }},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("signrawtransactionwithwallet", "\"myhex\"")
@@ -3368,13 +3396,16 @@ static UniValue bumpfee(const JSONRPCRequest& request)
"options"},
},
RPCResult{
- "{\n"
- " \"psbt\": \"psbt\", (string) The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled.\n"
- " \"txid\": \"value\", (string) The id of the new transaction. Only returned when wallet private keys are enabled.\n"
- " \"origfee\": n, (numeric) The fee of the replaced transaction.\n"
- " \"fee\": n, (numeric) The fee of the new transaction.\n"
- " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty).\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "", {
+ {RPCResult::Type::STR, "psbt", "The base64-encoded unsigned PSBT of the new transaction. Only returned when wallet private keys are disabled."},
+ {RPCResult::Type::STR_HEX, "txid", "The id of the new transaction. Only returned when wallet private keys are enabled."},
+ {RPCResult::Type::STR_AMOUNT, "origfee", "The fee of the replaced transaction."},
+ {RPCResult::Type::STR_AMOUNT, "fee", "The fee of the new transaction."},
+ {RPCResult::Type::ARR, "errors", "Errors encountered during processing (may be empty).",
+ {
+ {RPCResult::Type::STR, "", ""},
+ }},
+ }
},
RPCExamples{
"\nBump the fee, get the new transaction\'s txid\n" +
@@ -3529,10 +3560,11 @@ UniValue rescanblockchain(const JSONRPCRequest& request)
{"stop_height", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "the last block height that should be scanned. If none is provided it will rescan up to the tip at return time of this call."},
},
RPCResult{
- "{\n"
- " \"start_height\" (numeric) The block height where the rescan started (the requested height or 0)\n"
- " \"stop_height\" (numeric) The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background.\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::NUM, "start_height", "The block height where the rescan started (the requested height or 0)"},
+ {RPCResult::Type::NUM, "stop_height", "The height of the last rescanned block. May be null in rare cases if there was a reorg and the call didn't scan any blocks because they were already scanned in the background."},
+ }
},
RPCExamples{
HelpExampleCli("rescanblockchain", "100000 120000")
@@ -3730,8 +3762,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
return NullUniValue;
}
- const std::string example_address = "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"";
-
RPCHelpMan{"getaddressinfo",
"\nReturn information about the given bitcoin address.\n"
"Some of the information will only be present if the address is in the active wallet.\n",
@@ -3739,53 +3769,56 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
},
RPCResult{
- "{\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.\n"
- " \"iswatchonly\" : true|false, (boolean) If the address is watchonly.\n"
- " \"solvable\" : true|false, (boolean) If we know how to spend coins sent to this address, ignoring the possible lack of private keys.\n"
- " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable).\n"
- " \"isscript\" : true|false, (boolean) If the key is a script.\n"
- " \"ischange\" : true|false, (boolean) If the address was used for change output.\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\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "address", "The bitcoin address validated."},
+ {RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address."},
+ {RPCResult::Type::BOOL, "ismine", "If the address is yours."},
+ {RPCResult::Type::BOOL, "iswatchonly", "If the address is watchonly."},
+ {RPCResult::Type::BOOL, "solvable", "If we know how to spend coins sent to this address, ignoring the possible lack of private keys."},
+ {RPCResult::Type::STR, "desc", /* optional */ true, "A descriptor for spending coins sent to this address (only when solvable)."},
+ {RPCResult::Type::BOOL, "isscript", "If the key is a script."},
+ {RPCResult::Type::BOOL, "ischange", "If the address was used for change output."},
+ {RPCResult::Type::BOOL, "iswitness", "If the address is a witness address."},
+ {RPCResult::Type::NUM, "witness_version", /* optional */ true, "The version number of the witness program."},
+ {RPCResult::Type::STR_HEX, "witness_program", /* optional */ true, "The hex value of the witness program."},
+ {RPCResult::Type::STR, "script", /* optional */ true, "The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
" types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
- " witness_v0_scripthash, witness_unknown.\n"
- " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address.\n"
- " \"pubkeys\" (array, optional) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n"
- " [\n"
- " \"pubkey\" (string)\n"
- " ,...\n"
- " ]\n"
- " \"sigsrequired\" : xxxxx (numeric, optional) The 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. Includes all\n"
+ "witness_v0_scripthash, witness_unknown."},
+ {RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
+ {RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
+ {
+ {RPCResult::Type::STR, "pubkey", ""},
+ }},
+ {RPCResult::Type::NUM, "sigsrequired", /* optional */ true, "The number of signatures required to spend multisig output (only if script is multisig)."},
+ {RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
+ {RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
+ {
+ {RPCResult::Type::ELISION, "", "Includes all\n"
" getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
- " hdseedid) and relation to the wallet (ismine, iswatchonly).\n"
- " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed.\n"
- " \"label\" : \"label\" (string) The label associated with the address. Defaults to \"\". Equivalent to the label name in the labels array below.\n"
- " \"timestamp\" : timestamp, (number, optional) The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + ".\n"
- " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n"
- " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n"
- " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n"
- " \"labels\" (json object) An array of labels associated with the address. Currently limited to one label but returned\n"
- " as an array to keep the API stable if multiple labels are enabled in the future.\n"
- " [\n"
- " \"label name\" (string) The label name. Defaults to \"\". Equivalent to the label field above.\n\n"
- " DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`:\n"
- " { (json object of label data)\n"
- " \"name\" : \"label name\" (string) The label name. Defaults to \"\". Equivalent to the label field above.\n"
- " \"purpose\" : \"purpose\" (string) The purpose of the associated address (send or receive).\n"
- " }\n"
- " ]\n"
- "}\n"
+ "hdseedid) and relation to the wallet (ismine, iswatchonly)."},
+ }},
+ {RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."},
+ {RPCResult::Type::STR, "label", "DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below."},
+ {RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
+ {RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."},
+ {RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."},
+ {RPCResult::Type::STR_HEX, "hdmasterfingerprint", /* optional */ true, "The fingerprint of the master key."},
+ {RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
+ "as an array to keep the API stable if multiple labels are enabled in the future.",
+ {
+ {RPCResult::Type::STR, "label name", "The label name. Defaults to \"\"."},
+ {RPCResult::Type::OBJ, "", "label data, DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`",
+ {
+ {RPCResult::Type::STR, "name", "The label name. Defaults to \"\"."},
+ {RPCResult::Type::STR, "purpose", "The purpose of the associated address (send or receive)."},
+ }},
+ }},
+ }
},
RPCExamples{
- HelpExampleCli("getaddressinfo", example_address) +
- HelpExampleRpc("getaddressinfo", example_address)
+ HelpExampleCli("getaddressinfo", EXAMPLE_ADDRESS) +
+ HelpExampleRpc("getaddressinfo", EXAMPLE_ADDRESS)
},
}.Check(request);
@@ -3821,10 +3854,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
- // Return label field if existing. Currently only one label can be
- // associated with an address, so the label should be equivalent to the
+ // DEPRECATED: Return label field if existing. Currently only one label can
+ // be associated with an address, so the label should be equivalent to the
// value of the name key/value pair in the labels array below.
- if (pwallet->mapAddressBook.count(dest)) {
+ if ((pwallet->chain().rpcEnableDeprecated("label")) && (pwallet->mapAddressBook.count(dest))) {
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
}
@@ -3847,12 +3880,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// associated with an address, but we return an array so the API remains
// stable if we allow multiple labels to be associated with an address in
// the future.
- //
- // DEPRECATED: The previous behavior of returning an array containing a JSON
- // object of `name` and `purpose` key/value pairs has been deprecated.
UniValue labels(UniValue::VARR);
std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end()) {
+ // DEPRECATED: The previous behavior of returning an array containing a
+ // JSON object of `name` and `purpose` key/value pairs is deprecated.
if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
labels.push_back(AddressBookDataToJSON(mi->second, true));
} else {
@@ -3879,11 +3911,13 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
{"label", RPCArg::Type::STR, RPCArg::Optional::NO, "The label."},
},
RPCResult{
- "{ (json object with addresses as keys)\n"
- " \"address\": { (json object with information about address)\n"
- " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n"
- " },...\n"
- "}\n"
+ RPCResult::Type::OBJ_DYN, "", "json object with addresses as keys",
+ {
+ {RPCResult::Type::OBJ, "address", "json object with information about address",
+ {
+ {RPCResult::Type::STR, "purpose", "Purpose of address (\"send\" for sending address, \"receive\" for receiving address)"},
+ }},
+ }
},
RPCExamples{
HelpExampleCli("getaddressesbylabel", "\"tabby\"")
@@ -3936,10 +3970,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
{"purpose", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Address purpose to list labels for ('send','receive'). An empty string is the same as not providing this argument."},
},
RPCResult{
- "[ (json array of string)\n"
- " \"label\", (string) Label name\n"
- " ...\n"
- "]\n"
+ RPCResult::Type::ARR, "", "",
+ {
+ {RPCResult::Type::STR, "label", "Label name"},
+ }
},
RPCExamples{
"\nList all labels\n"
@@ -4077,14 +4111,14 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
" \"ALL|ANYONECANPAY\"\n"
" \"NONE|ANYONECANPAY\"\n"
" \"SINGLE|ANYONECANPAY\""},
- {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
- "{\n"
- " \"psbt\" : \"value\", (string) The base64-encoded partially signed transaction\n"
- " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n"
- " ]\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "psbt", "The base64-encoded partially signed transaction"},
+ {RPCResult::Type::BOOL, "complete", "If the transaction has a complete set of signatures"},
+ }
},
RPCExamples{
HelpExampleCli("walletprocesspsbt", "\"psbt\"")
@@ -4105,7 +4139,7 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
// Fill transaction with our data and also sign
bool sign = request.params[1].isNull() ? true : request.params[1].get_bool();
- bool bip32derivs = request.params[3].isNull() ? false : request.params[3].get_bool();
+ bool bip32derivs = request.params[3].isNull() ? true : request.params[3].get_bool();
bool complete = true;
const TransactionError err = FillPSBT(pwallet, psbtx, complete, nHashType, sign, bip32derivs);
if (err != TransactionError::OK) {
@@ -4188,14 +4222,15 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
" \"CONSERVATIVE\""},
},
"options"},
- {"bip32derivs", RPCArg::Type::BOOL, /* default */ "false", "If true, includes the BIP 32 derivation paths for public keys if we know them"},
+ {"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
},
RPCResult{
- "{\n"
- " \"psbt\": \"value\", (string) The resulting raw transaction (base64-encoded string)\n"
- " \"fee\": n, (numeric) Fee in " + CURRENCY_UNIT + " the resulting transaction pays\n"
- " \"changepos\": n (numeric) The position of the added change output, or -1\n"
- "}\n"
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::STR, "psbt", "The resulting raw transaction (base64-encoded string)"},
+ {RPCResult::Type::STR_AMOUNT, "fee", "Fee in " + CURRENCY_UNIT + " the resulting transaction pays"},
+ {RPCResult::Type::NUM, "changepos", "The position of the added change output, or -1"},
+ }
},
RPCExamples{
"\nCreate a transaction with no inputs\n"
@@ -4227,7 +4262,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
PartiallySignedTransaction psbtx(rawTx);
// Fill transaction with out data but don't sign
- bool bip32derivs = request.params[4].isNull() ? false : request.params[4].get_bool();
+ bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
bool complete = true;
const TransactionError err = FillPSBT(pwallet, psbtx, complete, 1, false, bip32derivs);
if (err != TransactionError::OK) {
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 4c9d88973e..0c95ab29b1 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -70,7 +70,15 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const LegacyScriptPubKeyMan&
return true;
}
-IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
+//! Recursively solve script and return spendable/watchonly/invalid status.
+//!
+//! @param keystore legacy key and script store
+//! @param script script to solve
+//! @param sigversion script type (top-level / redeemscript / witnessscript)
+//! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh
+//! scripts or simply treat any script that has been
+//! stored in the keystore as spendable
+IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true)
{
IsMineResult ret = IsMineResult::NO;
@@ -129,7 +137,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
+ ret = std::max(ret, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::P2SH) : IsMineResult::SPENDABLE);
}
break;
}
@@ -147,7 +155,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
CScriptID scriptID = CScriptID(hash);
CScript subscript;
if (keystore.GetCScript(scriptID, subscript)) {
- ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
+ ret = std::max(ret, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0) : IsMineResult::SPENDABLE);
}
break;
}
@@ -476,11 +484,11 @@ std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSigningProvider(const
bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata)
{
- if (IsMine(script) != ISMINE_NO) {
- // If it IsMine, we can always provide in some way
- return true;
- } else if (HaveCScript(CScriptID(script))) {
- // We can still provide some stuff if we have the script, but IsMine failed because we don't have keys
+ IsMineResult ismine = IsMineInner(*this, script, IsMineSigVersion::TOP, /* recurse_scripthash= */ false);
+ if (ismine == IsMineResult::SPENDABLE || ismine == IsMineResult::WATCH_ONLY) {
+ // If ismine, it means we recognize keys or script ids in the script, or
+ // are watching the script itself, and we can at least provide metadata
+ // or solving information, even if not able to sign fully.
return true;
} else {
// If, given the stuff in sigdata, we could make a valid sigature, then we can provide for this script
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index d65a0e9075..21d57cb898 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -189,6 +189,19 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
actual_selection.clear();
selection.clear();
+ // Cost of change is greater than the difference between target value and utxo sum
+ add_coin(1 * CENT, 1, actual_selection);
+ BOOST_CHECK(SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT, selection, value_ret, not_input_fees));
+ BOOST_CHECK_EQUAL(value_ret, 1 * CENT);
+ BOOST_CHECK(equal_sets(selection, actual_selection));
+ actual_selection.clear();
+ selection.clear();
+
+ // Cost of change is less than the difference between target value and utxo sum
+ BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0, selection, value_ret, not_input_fees));
+ actual_selection.clear();
+ selection.clear();
+
// Select 10 Cent
add_coin(5 * CENT, 5, utxo_pool);
add_coin(4 * CENT, 4, actual_selection);
diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp
new file mode 100644
index 0000000000..757865ea37
--- /dev/null
+++ b/src/wallet/test/scriptpubkeyman_tests.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <key.h>
+#include <script/standard.h>
+#include <test/util/setup_common.h>
+#include <wallet/scriptpubkeyman.h>
+#include <wallet/wallet.h>
+
+#include <boost/test/unit_test.hpp>
+
+BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup)
+
+// Test LegacyScriptPubKeyMan::CanProvide behavior, making sure it returns true
+// for recognized scripts even when keys may not be available for signing.
+BOOST_AUTO_TEST_CASE(CanProvide)
+{
+ // Set up wallet and keyman variables.
+ NodeContext node;
+ std::unique_ptr<interfaces::Chain> chain = interfaces::MakeChain(node);
+ CWallet wallet(chain.get(), WalletLocation(), WalletDatabase::CreateDummy());
+ LegacyScriptPubKeyMan& keyman = *wallet.GetOrCreateLegacyScriptPubKeyMan();
+
+ // Make a 1 of 2 multisig script
+ std::vector<CKey> keys(2);
+ std::vector<CPubKey> pubkeys;
+ for (CKey& key : keys) {
+ key.MakeNewKey(true);
+ pubkeys.emplace_back(key.GetPubKey());
+ }
+ CScript multisig_script = GetScriptForMultisig(1, pubkeys);
+ CScript p2sh_script = GetScriptForDestination(ScriptHash(multisig_script));
+ SignatureData data;
+
+ // Verify the p2sh(multisig) script is not recognized until the multisig
+ // script is added to the keystore to make it solvable
+ BOOST_CHECK(!keyman.CanProvide(p2sh_script, data));
+ keyman.AddCScript(multisig_script);
+ BOOST_CHECK(keyman.CanProvide(p2sh_script, data));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4a38571dfc..5a97f62b71 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -13,6 +13,7 @@
#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
+#include <optional.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <primitives/block.h>
@@ -26,7 +27,6 @@
#include <util/moneystr.h>
#include <util/rbf.h>
#include <util/translation.h>
-#include <util/validation.h>
#include <wallet/coincontrol.h>
#include <wallet/fees.h>
@@ -713,7 +713,7 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
return success;
}
-void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
+void CWallet::SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations)
{
AssertLockHeld(cs_wallet);
const CWalletTx* srctx = GetWalletTx(hash);
@@ -733,7 +733,7 @@ void CWallet::SetUsedDestinationState(WalletBatch& batch, const uint256& hash, u
}
}
-bool CWallet::IsUsedDestination(const uint256& hash, unsigned int n) const
+bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const
{
AssertLockHeld(cs_wallet);
CTxDestination dst;
@@ -776,7 +776,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
for (const CTxIn& txin : wtxIn.tx->vin) {
const COutPoint& op = txin.prevout;
- SetUsedDestinationState(batch, op.hash, op.n, true, tx_destinations);
+ SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations);
}
MarkDestinationsDirty(tx_destinations);
@@ -846,6 +846,14 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());
+#ifndef WIN32
+ // Substituting the wallet name isn't currently supported on windows
+ // because windows shell escaping has not been implemented yet:
+ // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-537384875
+ // A few ways it could be implemented in the future are described in:
+ // https://github.com/bitcoin/bitcoin/pull/13339#issuecomment-461288094
+ boost::replace_all(strCmd, "%w", ShellEscape(GetName()));
+#endif
std::thread t(runCommand, strCmd);
t.detach(); // thread runs free
}
@@ -1869,7 +1877,7 @@ CAmount CWalletTx::GetAvailableCredit(bool fUseCache, const isminefilter& filter
uint256 hashTx = GetHash();
for (unsigned int i = 0; i < tx->vout.size(); i++)
{
- if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsUsedDestination(hashTx, i))) {
+ if (!pwallet->IsSpent(hashTx, i) && (allow_used_addresses || !pwallet->IsSpentKey(hashTx, i))) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, filter);
if (!MoneyRange(nCredit))
@@ -2159,7 +2167,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- if (!allow_used_addresses && IsUsedDestination(wtxid, i)) {
+ if (!allow_used_addresses && IsSpentKey(wtxid, i)) {
continue;
}
@@ -3910,7 +3918,8 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
- Optional<int64_t> time_first_key;
+ // The way the 'time_first_key' is initialized is just a workaround for the gcc bug #47679 since version 4.6.0.
+ Optional<int64_t> time_first_key = MakeOptional(false, int64_t());;
for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
int64_t time = spk_man->GetTimeFirstKey();
if (!time_first_key || time < *time_first_key) time_first_key = time;
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a918bb8833..6c1c3040c2 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -817,8 +817,8 @@ public:
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
// Whether this or any known UTXO with the same single key has been spent.
- bool IsUsedDestination(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- void SetUsedDestinationState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool single_coin) const;