aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/coincontrol.h2
-rw-r--r--src/wallet/crypter.cpp2
-rw-r--r--src/wallet/crypter.h2
-rw-r--r--src/wallet/db.cpp10
-rw-r--r--src/wallet/db.h14
-rw-r--r--src/wallet/rpcdump.cpp20
-rw-r--r--src/wallet/rpcwallet.cpp683
-rw-r--r--src/wallet/test/accounting_tests.cpp2
-rw-r--r--src/wallet/test/crypto_tests.cpp2
-rw-r--r--src/wallet/test/wallet_tests.cpp88
-rw-r--r--src/wallet/wallet.cpp439
-rw-r--r--src/wallet/wallet.h65
-rw-r--r--src/wallet/walletdb.cpp5
-rw-r--r--src/wallet/walletdb.h4
14 files changed, 996 insertions, 342 deletions
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 08d23688ff..eaf4ff8062 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2011-2015 The Bitcoin Core developers
+// Copyright (c) 2011-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp
index 31ee060677..fc318c1612 100644
--- a/src/wallet/crypter.cpp
+++ b/src/wallet/crypter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h
index e89c15b5d4..275e435f73 100644
--- a/src/wallet/crypter.h
+++ b/src/wallet/crypter.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp
index a809c9ad64..800e1f4e29 100644
--- a/src/wallet/db.cpp
+++ b/src/wallet/db.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -397,15 +397,15 @@ bool CDB::Rewrite(const string& strFile, const char* pszSkip)
break;
}
if (pszSkip &&
- strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
+ strncmp(ssKey.data(), pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
continue;
- if (strncmp(&ssKey[0], "\x07version", 8) == 0) {
+ if (strncmp(ssKey.data(), "\x07version", 8) == 0) {
// Update version:
ssValue.clear();
ssValue << CLIENT_VERSION;
}
- Dbt datKey(&ssKey[0], ssKey.size());
- Dbt datValue(&ssValue[0], ssValue.size());
+ Dbt datKey(ssKey.data(), ssKey.size());
+ Dbt datValue(ssValue.data(), ssValue.size());
int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
if (ret2 > 0)
fSuccess = false;
diff --git a/src/wallet/db.h b/src/wallet/db.h
index a0f673ecfc..bc15f2147f 100644
--- a/src/wallet/db.h
+++ b/src/wallet/db.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -122,7 +122,7 @@ protected:
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- Dbt datKey(&ssKey[0], ssKey.size());
+ Dbt datKey(ssKey.data(), ssKey.size());
// Read
Dbt datValue;
@@ -158,13 +158,13 @@ protected:
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- Dbt datKey(&ssKey[0], ssKey.size());
+ Dbt datKey(ssKey.data(), ssKey.size());
// Value
CDataStream ssValue(SER_DISK, CLIENT_VERSION);
ssValue.reserve(10000);
ssValue << value;
- Dbt datValue(&ssValue[0], ssValue.size());
+ Dbt datValue(ssValue.data(), ssValue.size());
// Write
int ret = pdb->put(activeTxn, &datKey, &datValue, (fOverwrite ? 0 : DB_NOOVERWRITE));
@@ -187,7 +187,7 @@ protected:
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- Dbt datKey(&ssKey[0], ssKey.size());
+ Dbt datKey(ssKey.data(), ssKey.size());
// Erase
int ret = pdb->del(activeTxn, &datKey, 0);
@@ -207,7 +207,7 @@ protected:
CDataStream ssKey(SER_DISK, CLIENT_VERSION);
ssKey.reserve(1000);
ssKey << key;
- Dbt datKey(&ssKey[0], ssKey.size());
+ Dbt datKey(ssKey.data(), ssKey.size());
// Exists
int ret = pdb->exists(activeTxn, &datKey, 0);
@@ -234,7 +234,7 @@ protected:
Dbt datKey;
unsigned int fFlags = DB_NEXT;
if (setRange) {
- datKey.set_data(&ssKey[0]);
+ datKey.set_data(ssKey.data());
datKey.set_size(ssKey.size());
fFlags = DB_SET_RANGE;
}
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index 008a4ece19..7d4ed70ed9 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -82,7 +82,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw runtime_error(
- "importprivkey \"bitcoinprivkey\" ( \"label\" rescan )\n"
+ "importprivkey \"bitcoinprivkey\" ( \"label\" ) ( rescan )\n"
"\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
"\nArguments:\n"
"1. \"bitcoinprivkey\" (string, required) The private key (see dumpprivkey)\n"
@@ -520,11 +520,11 @@ UniValue dumpprivkey(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw runtime_error(
- "dumpprivkey \"bitcoinaddress\"\n"
- "\nReveals the private key corresponding to 'bitcoinaddress'.\n"
+ "dumpprivkey \"address\"\n"
+ "\nReveals the private key corresponding to 'address'.\n"
"Then the importprivkey can be used with this output\n"
"\nArguments:\n"
- "1. \"bitcoinaddress\" (string, required) The bitcoin address for the private key\n"
+ "1. \"address\" (string, required) The bitcoin address for the private key\n"
"\nResult:\n"
"\"key\" (string) The private key\n"
"\nExamples:\n"
@@ -963,10 +963,10 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
// clang-format off
if (mainRequest.fHelp || mainRequest.params.size() < 1 || mainRequest.params.size() > 2)
throw runtime_error(
- "importmulti '[<json import requests>]' '<json options>' \n\n"
+ "importmulti \"requests\" \"options\"\n\n"
"Import addresses/scripts (with private or public keys, redeem script (P2SH)), rescanning all addresses in one-shot-only (rescan can be disabled via options).\n\n"
"Arguments:\n"
- "1. request array (array, required) Data to be imported\n"
+ "1. requests (array, required) Data to be imported\n"
" [ (array of json objects)\n"
" {\n"
" \"scriptPubKey\": \"<script>\" | { \"address\":\"<address>\" }, (string / json, required) Type of scriptPubKey (string for script, json for address)\n"
@@ -980,7 +980,7 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
" }\n"
" ,...\n"
" ]\n"
- "2. json options (json, optional)\n"
+ "2. options (json, optional)\n"
" {\n"
" \"rescan\": <false>, (boolean, optional, default: true) Stating if should rescan the blockchain after all imports\n"
" }\n"
@@ -1048,8 +1048,8 @@ UniValue importmulti(const JSONRPCRequest& mainRequest)
}
}
- if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTime()) {
- CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindLatestBefore(nLowestTimestamp) : chainActive.Genesis();
+ if (fRescan && fRunScan && requests.size() && nLowestTimestamp <= chainActive.Tip()->GetBlockTimeMax()) {
+ CBlockIndex* pindex = nLowestTimestamp > minimumTimestamp ? chainActive.FindEarliestAtLeast(nLowestTimestamp) : chainActive.Genesis();
if (pindex) {
pwalletMain->ScanForWalletTransactions(pindex, true);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 5a4fcc743c..45b572aa2e 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -11,8 +11,10 @@
#include "init.h"
#include "validation.h"
#include "net.h"
+#include "policy/policy.h"
#include "policy/rbf.h"
#include "rpc/server.h"
+#include "script/sign.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
@@ -116,7 +118,7 @@ UniValue getnewaddress(const JSONRPCRequest& request)
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. The account name for the address to be linked to. If not provided, the default account \"\" is used. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created if there is no account by the given name.\n"
"\nResult:\n"
- "\"bitcoinaddress\" (string) The new bitcoin address\n"
+ "\"address\" (string) The new bitcoin address\n"
"\nExamples:\n"
+ HelpExampleCli("getnewaddress", "")
+ HelpExampleRpc("getnewaddress", "")
@@ -166,7 +168,7 @@ UniValue getaccountaddress(const JSONRPCRequest& request)
"\nArguments:\n"
"1. \"account\" (string, required) The account name for the address. It can also be set to the empty string \"\" to represent the default account. The account does not need to exist, it will be created and a new address created if there is no account by the given name.\n"
"\nResult:\n"
- "\"bitcoinaddress\" (string) The account bitcoin address\n"
+ "\"address\" (string) The account bitcoin address\n"
"\nExamples:\n"
+ HelpExampleCli("getaccountaddress", "")
+ HelpExampleCli("getaccountaddress", "\"\"")
@@ -228,10 +230,10 @@ UniValue setaccount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
- "setaccount \"bitcoinaddress\" \"account\"\n"
+ "setaccount \"address\" \"account\"\n"
"\nDEPRECATED. Sets the account associated with the given address.\n"
"\nArguments:\n"
- "1. \"bitcoinaddress\" (string, required) The bitcoin address to be associated with an account.\n"
+ "1. \"address\" (string, required) The bitcoin address to be associated with an account.\n"
"2. \"account\" (string, required) The account to assign the address to.\n"
"\nExamples:\n"
+ HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"tabby\"")
@@ -274,10 +276,10 @@ UniValue getaccount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 1)
throw runtime_error(
- "getaccount \"bitcoinaddress\"\n"
+ "getaccount \"address\"\n"
"\nDEPRECATED. Returns the account associated with the given address.\n"
"\nArguments:\n"
- "1. \"bitcoinaddress\" (string, required) The bitcoin address for account lookup.\n"
+ "1. \"address\" (string, required) The bitcoin address for account lookup.\n"
"\nResult:\n"
"\"accountname\" (string) the account address\n"
"\nExamples:\n"
@@ -309,10 +311,10 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
"getaddressesbyaccount \"account\"\n"
"\nDEPRECATED. Returns the list of addresses for the given account.\n"
"\nArguments:\n"
- "1. \"account\" (string, required) The account name.\n"
+ "1. \"account\" (string, required) The account name.\n"
"\nResult:\n"
"[ (json array of string)\n"
- " \"bitcoinaddress\" (string) a bitcoin address associated with the given account\n"
+ " \"address\" (string) a bitcoin address associated with the given account\n"
" ,...\n"
"]\n"
"\nExamples:\n"
@@ -380,21 +382,21 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 2 || request.params.size() > 5)
throw runtime_error(
- "sendtoaddress \"bitcoinaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n"
+ "sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" subtractfeefromamount )\n"
"\nSend an amount to a given address.\n"
+ HelpRequiringPassphrase() +
"\nArguments:\n"
- "1. \"bitcoinaddress\" (string, required) The bitcoin address to send to.\n"
- "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n"
- "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
+ "1. \"address\" (string, required) The bitcoin address to send to.\n"
+ "2. \"amount\" (numeric or string, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n"
+ "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
" This is not part of the transaction, just kept in your wallet.\n"
- "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n"
+ "4. \"comment_to\" (string, optional) A comment to store the name of the person or organization \n"
" to which you're sending the transaction. This is not part of the \n"
" transaction, just kept in your wallet.\n"
"5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
" The recipient will receive less bitcoins than you enter in the amount field.\n"
"\nResult:\n"
- "\"transactionid\" (string) The transaction id.\n"
+ "\"txid\" (string) The transaction id.\n"
"\nExamples:\n"
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
+ HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"")
@@ -446,9 +448,9 @@ UniValue listaddressgroupings(const JSONRPCRequest& request)
"[\n"
" [\n"
" [\n"
- " \"bitcoinaddress\", (string) The bitcoin address\n"
+ " \"address\", (string) The bitcoin address\n"
" amount, (numeric) The amount in " + CURRENCY_UNIT + "\n"
- " \"account\" (string, optional) The account (DEPRECATED)\n"
+ " \"account\" (string, optional) DEPRECATED. The account\n"
" ]\n"
" ,...\n"
" ]\n"
@@ -489,11 +491,11 @@ UniValue signmessage(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() != 2)
throw runtime_error(
- "signmessage \"bitcoinaddress\" \"message\"\n"
+ "signmessage \"address\" \"message\"\n"
"\nSign a message with the private key of an address"
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
- "1. \"bitcoinaddress\" (string, required) The bitcoin address to use for the private key.\n"
+ "1. \"address\" (string, required) The bitcoin address to use for the private key.\n"
"2. \"message\" (string, required) The message to create a signature of.\n"
"\nResult:\n"
"\"signature\" (string) The signature of the message encoded in base 64\n"
@@ -545,10 +547,10 @@ UniValue getreceivedbyaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
- "getreceivedbyaddress \"bitcoinaddress\" ( minconf )\n"
- "\nReturns the total amount received by the given bitcoinaddress in transactions with at least minconf confirmations.\n"
+ "getreceivedbyaddress \"address\" ( minconf )\n"
+ "\nReturns the total amount received by the given address in transactions with at least minconf confirmations.\n"
"\nArguments:\n"
- "1. \"bitcoinaddress\" (string, required) The bitcoin address for transactions.\n"
+ "1. \"address\" (string, required) The bitcoin address for transactions.\n"
"2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n"
@@ -660,15 +662,26 @@ UniValue getbalance(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 3)
throw runtime_error(
- "getbalance ( \"account\" minconf includeWatchonly )\n"
+ "getbalance ( \"account\" minconf include_watchonly )\n"
"\nIf account is not specified, returns the server's total available balance.\n"
"If account is specified (DEPRECATED), returns the balance in the account.\n"
"Note that the account \"\" is not the same as leaving the parameter out.\n"
"The server total may be different to the balance in the default \"\" account.\n"
"\nArguments:\n"
- "1. \"account\" (string, optional) DEPRECATED. The selected account, or \"*\" for entire wallet. It may be the default account using \"\".\n"
- "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
- "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n"
+ "1. \"account\" (string, optional) DEPRECATED. The account string may be given as a\n"
+ " specific account name to find the balance associated with wallet keys in\n"
+ " a named account, or as the empty string (\"\") to find the balance\n"
+ " associated with wallet keys not in any named account, or as \"*\" to find\n"
+ " the balance associated with all wallet keys regardless of account.\n"
+ " When this option is specified, it calculates the balance in a different\n"
+ " way than when it is not specified, and which can count spends twice when\n"
+ " there are conflicting pending transactions (such as those created by\n"
+ " the bumpfee command), temporarily resulting in low or even negative\n"
+ " balances. In general, account balance calculation is not considered\n"
+ " reliable and has resulted in confusing outcomes, so it is recommended to\n"
+ " avoid passing this argument.\n"
+ "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n"
+ "3. include_watchonly (bool, optional, default=false) Also include balance in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n"
"\nExamples:\n"
@@ -694,9 +707,12 @@ UniValue getbalance(const JSONRPCRequest& request)
filter = filter | ISMINE_WATCH_ONLY;
if (request.params[0].get_str() == "*") {
- // Calculate total balance a different way from GetBalance()
- // (GetBalance() sums up all unspent TxOuts)
- // getbalance and "getbalance * 1 true" should return the same number
+ // Calculate total balance in a very different way from GetBalance().
+ // The biggest difference is that GetBalance() sums up all unspent
+ // TxOuts paying to the wallet, while this sums up both spent and
+ // unspent TxOuts paying to the wallet, and then subtracts the values of
+ // TxIns spending from the wallet. This also has fewer restrictions on
+ // which unconfirmed transactions are considered trusted.
CAmount nBalance = 0;
for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it)
{
@@ -757,7 +773,7 @@ UniValue movecmd(const JSONRPCRequest& request)
"1. \"fromaccount\" (string, required) The name of the account to move funds from. May be the default account using \"\".\n"
"2. \"toaccount\" (string, required) The name of the account to move funds to. May be the default account using \"\".\n"
"3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n"
- "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n"
+ "4. (dummy) (numeric, optional) Ignored. Remains for backward compatibility.\n"
"5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n"
"\nResult:\n"
"true|false (boolean) true if successful.\n"
@@ -798,21 +814,21 @@ UniValue sendfrom(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 3 || request.params.size() > 6)
throw runtime_error(
- "sendfrom \"fromaccount\" \"tobitcoinaddress\" amount ( minconf \"comment\" \"comment-to\" )\n"
+ "sendfrom \"fromaccount\" \"toaddress\" amount ( minconf \"comment\" \"comment_to\" )\n"
"\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a bitcoin address."
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The name of the account to send funds from. May be the default account using \"\".\n"
- "2. \"tobitcoinaddress\" (string, required) The bitcoin address to send funds to.\n"
+ "2. \"toaddress\" (string, required) The bitcoin address to send funds to.\n"
"3. amount (numeric or string, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n"
"4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n"
"5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n"
" This is not part of the transaction, just kept in your wallet.\n"
- "6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n"
+ "6. \"comment_to\" (string, optional) An optional comment to store the name of the person or organization \n"
" to which you're sending the transaction. This is not part of the transaction, \n"
" it is just kept in your wallet.\n"
"\nResult:\n"
- "\"transactionid\" (string) The transaction id.\n"
+ "\"txid\" (string) The transaction id.\n"
"\nExamples:\n"
"\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n"
+ HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") +
@@ -874,16 +890,16 @@ UniValue sendmany(const JSONRPCRequest& request)
" }\n"
"3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n"
"4. \"comment\" (string, optional) A comment\n"
- "5. subtractfeefromamount (string, optional) A json array with addresses.\n"
+ "5. subtractfeefrom (array, optional) A json array with addresses.\n"
" The fee will be equally deducted from the amount of each selected address.\n"
" Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
" If no addresses are specified here, the sender pays the fee.\n"
" [\n"
- " \"address\" (string) Subtract fee from this address\n"
+ " \"address\" (string) Subtract fee from this address\n"
" ,...\n"
" ]\n"
"\nResult:\n"
- "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
+ "\"txid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
"\nExamples:\n"
"\nSend two amounts to two different addresses:\n"
@@ -989,7 +1005,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n"
- "2. \"keysobject\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n"
+ "2. \"keys\" (string, required) A json array of bitcoin addresses or hex-encoded public keys\n"
" [\n"
" \"address\" (string) bitcoin address or hex-encoded public key\n"
" ...,\n"
@@ -997,7 +1013,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
"3. \"account\" (string, optional) DEPRECATED. An account to assign the addresses to.\n"
"\nResult:\n"
- "\"bitcoinaddress\" (string) A bitcoin address associated with the keys.\n"
+ "\"address\" (string) A bitcoin address associated with the keys.\n"
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n"
@@ -1253,12 +1269,12 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 3)
throw runtime_error(
- "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n"
+ "listreceivedbyaddress ( minconf include_empty include_watchonly)\n"
"\nList balances by receiving address.\n"
"\nArguments:\n"
- "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
- "2. includeempty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
- "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
+ "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
+ "2. include_empty (bool, optional, default=false) Whether to include addresses that haven't received any payments.\n"
+ "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
@@ -1268,7 +1284,11 @@ UniValue listreceivedbyaddress(const JSONRPCRequest& request)
" \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\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) A comment for the address/transaction, if any\n"
+ " \"label\" : \"label\", (string) A comment for the address/transaction, if any\n"
+ " \"txids\": [\n"
+ " n, (numeric) The ids of transactions received with the address \n"
+ " ...\n"
+ " ]\n"
" }\n"
" ,...\n"
"]\n"
@@ -1291,12 +1311,12 @@ UniValue listreceivedbyaccount(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 3)
throw runtime_error(
- "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n"
+ "listreceivedbyaccount ( minconf include_empty include_watchonly)\n"
"\nDEPRECATED. List balances by account.\n"
"\nArguments:\n"
- "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
- "2. includeempty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n"
- "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n"
+ "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n"
+ "2. include_empty (bool, optional, default=false) Whether to include accounts that haven't received any payments.\n"
+ "3. include_watchonly (bool, optional, default=false) Whether to include watch-only addresses (see 'importaddress').\n"
"\nResult:\n"
"[\n"
@@ -1427,19 +1447,19 @@ UniValue listtransactions(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 4)
throw runtime_error(
- "listtransactions ( \"account\" count from includeWatchonly)\n"
+ "listtransactions ( \"account\" count skip include_watchonly)\n"
"\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n"
"\nArguments:\n"
"1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n"
"2. count (numeric, optional, default=10) The number of transactions to return\n"
- "3. from (numeric, optional, default=0) The number of transactions to skip\n"
- "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n"
+ "3. skip (numeric, optional, default=0) The number of transactions to skip\n"
+ "4. include_watchonly (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"[\n"
" {\n"
" \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n"
" It will be \"\" for the default account.\n"
- " \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for \n"
+ " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for \n"
" move transactions (category = move).\n"
" \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n"
" transaction between accounts, and not associated with an address,\n"
@@ -1448,14 +1468,14 @@ UniValue listtransactions(const JSONRPCRequest& request)
" \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n"
" 'move' category for moves outbound. It is positive for the 'receive' category,\n"
" and for the 'move' category for inbound funds.\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).\n"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n"
" 'receive' category of transactions. Negative confirmations indicate the\n"
" transaction conflicts with the block chain\n"
- " \"trusted\": xxx (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n"
+ " \"trusted\": xxx, (bool) Whether we consider the outputs of this unconfirmed transaction safe to spend.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n"
" category of transactions.\n"
" \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive'\n"
@@ -1466,12 +1486,13 @@ UniValue listtransactions(const JSONRPCRequest& request)
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n"
" for 'send' and 'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is associated with the transaction.\n"
- " \"label\": \"label\" (string) A comment for the address/transaction, if any\n"
- " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n"
+ " \"otheraccount\": \"accountname\", (string) DEPRECATED. For the 'move' category of transactions, the account the funds came \n"
" from (for receiving funds, positive amounts), or went to (for sending funds,\n"
" negative amounts).\n"
- " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
+ " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
" may be unknown for unconfirmed transactions not in the mempool\n"
+ " \"abandoned\": xxx (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the \n"
+ " 'send' category of transactions.\n"
" }\n"
"]\n"
@@ -1554,11 +1575,11 @@ UniValue listaccounts(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() > 2)
throw runtime_error(
- "listaccounts ( minconf includeWatchonly)\n"
+ "listaccounts ( minconf include_watchonly)\n"
"\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n"
"\nArguments:\n"
- "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n"
- "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n"
+ "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n"
+ "2. include_watchonly (bool, optional, default=false) Include balances in watch-only addresses (see 'importaddress')\n"
"\nResult:\n"
"{ (json object where keys are account names, and values are numeric balances\n"
" \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n"
@@ -1633,29 +1654,33 @@ UniValue listsinceblock(const JSONRPCRequest& request)
if (request.fHelp)
throw runtime_error(
- "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n"
+ "listsinceblock ( \"blockhash\" target_confirmations include_watchonly)\n"
"\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n"
"\nArguments:\n"
- "1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
- "2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
- "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')"
+ "1. \"blockhash\" (string, optional) The block hash to list transactions since\n"
+ "2. target_confirmations: (numeric, optional) The confirmations required, must be 1 or more\n"
+ "3. include_watchonly: (bool, optional, default=false) Include transactions to watch-only addresses (see 'importaddress')"
"\nResult:\n"
"{\n"
" \"transactions\": [\n"
" \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n"
- " \"address\":\"bitcoinaddress\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
+ " \"address\":\"address\", (string) The bitcoin address of the transaction. Not present for move transactions (category = move).\n"
" \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n"
" \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n"
" outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\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"
" \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n"
+ " When it's < 0, it means the transaction conflicted that many blocks ago.\n"
" \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n"
" \"blockindex\": n, (numeric) The index of the transaction in the block that includes it. Available for 'send' and 'receive' category of transactions.\n"
" \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n"
" \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n"
" \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n"
+ " \"bip125-replaceable\": \"yes|no|unknown\", (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\n"
+ " may be unknown for unconfirmed transactions not in the mempool\n"
+ " \"abandoned\": xxx, (bool) 'true' if the transaction has been abandoned (inputs are respendable). Only available for the '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"
@@ -1670,7 +1695,7 @@ UniValue listsinceblock(const JSONRPCRequest& request)
LOCK2(cs_main, pwalletMain->cs_wallet);
- CBlockIndex *pindex = NULL;
+ const CBlockIndex *pindex = NULL;
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
@@ -1681,7 +1706,16 @@ UniValue listsinceblock(const JSONRPCRequest& request)
blockId.SetHex(request.params[0].get_str());
BlockMap::iterator it = mapBlockIndex.find(blockId);
if (it != mapBlockIndex.end())
+ {
pindex = it->second;
+ if (chainActive[pindex->nHeight] != pindex)
+ {
+ // the block being asked for is a part of a deactivated chain;
+ // we don't want to depend on its perceived height in the block
+ // chain, we want to instead use the last common ancestor
+ pindex = chainActive.FindFork(pindex);
+ }
+ }
}
if (request.params.size() > 1)
@@ -1692,9 +1726,10 @@ UniValue listsinceblock(const JSONRPCRequest& request)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
- if(request.params.size() > 2)
- if(request.params[2].get_bool())
- filter = filter | ISMINE_WATCH_ONLY;
+ if (request.params.size() > 2 && request.params[2].get_bool())
+ {
+ filter = filter | ISMINE_WATCH_ONLY;
+ }
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
@@ -1725,14 +1760,16 @@ UniValue gettransaction(const JSONRPCRequest& request)
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw runtime_error(
- "gettransaction \"txid\" ( includeWatchonly )\n"
+ "gettransaction \"txid\" ( include_watchonly )\n"
"\nGet detailed information about in-wallet transaction <txid>\n"
"\nArguments:\n"
- "1. \"txid\" (string, required) The transaction id\n"
- "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n"
+ "1. \"txid\" (string, required) The transaction id\n"
+ "2. \"include_watchonly\" (bool, optional, default=false) Whether to include watch-only addresses in balance calculation and details[]\n"
"\nResult:\n"
"{\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"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"blockhash\" : \"hash\", (string) The block hash\n"
" \"blockindex\" : xx, (numeric) The index of the transaction in the block that includes it\n"
@@ -1740,16 +1777,20 @@ UniValue gettransaction(const JSONRPCRequest& request)
" \"txid\" : \"transactionid\", (string) The transaction id.\n"
" \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n"
" \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n"
- " \"bip125-replaceable\": \"yes|no|unknown\" (string) Whether this transaction could be replaced due to BIP125 (replace-by-fee);\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"
" \"details\" : [\n"
" {\n"
- " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n"
- " \"address\" : \"bitcoinaddress\", (string) The bitcoin address involved in the transaction\n"
+ " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n"
+ " \"address\" : \"address\", (string) The bitcoin address involved in the transaction\n"
" \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n"
" \"amount\" : x.xxx, (numeric) The amount in " + CURRENCY_UNIT + "\n"
" \"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"
@@ -2070,7 +2111,7 @@ UniValue encryptwallet(const JSONRPCRequest& request)
"\nNow set the passphrase to use the wallet, such as for signing or sending bitcoin\n"
+ HelpExampleCli("walletpassphrase", "\"my pass phrase\"") +
"\nNow we can so something like sign\n"
- + HelpExampleCli("signmessage", "\"bitcoinaddress\" \"test message\"") +
+ + HelpExampleCli("signmessage", "\"address\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n"
+ HelpExampleCli("walletlock", "") +
"\nAs a json rpc call\n"
@@ -2290,7 +2331,7 @@ UniValue getwalletinfo(const JSONRPCRequest& request)
" \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n"
" \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that 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"
- " \"hdmasterkeyid\": \"<hash160>\", (string) the Hash160 of the HD master pubkey\n"
+ " \"hdmasterkeyid\": \"<hash160>\" (string) the Hash160 of the HD master pubkey\n"
"}\n"
"\nExamples:\n"
+ HelpExampleCli("getwalletinfo", "")
@@ -2349,9 +2390,9 @@ UniValue listunspent(const JSONRPCRequest& request)
if (!EnsureWalletIsAvailable(request.fHelp))
return NullUniValue;
- if (request.fHelp || request.params.size() > 3)
+ if (request.fHelp || request.params.size() > 4)
throw runtime_error(
- "listunspent ( minconf maxconf [\"address\",...] )\n"
+ "listunspent ( minconf maxconf [\"addresses\",...] [include_unsafe] )\n"
"\nReturns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
"Optionally filter to only include txouts paid to specified addresses.\n"
@@ -2363,6 +2404,10 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
+ "4. include_unsafe (bool, optional, default=true) Include outputs that are not safe to spend\n"
+ " because they come from unconfirmed untrusted transactions or unconfirmed\n"
+ " replacement transactions (cases where we are less sure that a conflicting\n"
+ " transaction won't be mined).\n"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
@@ -2371,7 +2416,7 @@ UniValue listunspent(const JSONRPCRequest& request)
" \"address\" : \"address\", (string) the bitcoin address\n"
" \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n"
" \"scriptPubKey\" : \"key\", (string) the script key\n"
- " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n"
+ " \"amount\" : x.xxx, (numeric) the transaction output amount in " + CURRENCY_UNIT + "\n"
" \"confirmations\" : n, (numeric) The number of confirmations\n"
" \"redeemScript\" : n (string) The redeemScript if scriptPubKey is P2SH\n"
" \"spendable\" : xxx, (bool) Whether we have the private keys to spend this output\n"
@@ -2386,18 +2431,21 @@ UniValue listunspent(const JSONRPCRequest& request)
+ HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"")
);
- RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR));
-
int nMinDepth = 1;
- if (request.params.size() > 0)
+ if (request.params.size() > 0 && !request.params[0].isNull()) {
+ RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
nMinDepth = request.params[0].get_int();
+ }
int nMaxDepth = 9999999;
- if (request.params.size() > 1)
+ if (request.params.size() > 1 && !request.params[1].isNull()) {
+ RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
nMaxDepth = request.params[1].get_int();
+ }
set<CBitcoinAddress> setAddress;
- if (request.params.size() > 2) {
+ if (request.params.size() > 2 && !request.params[2].isNull()) {
+ RPCTypeCheckArgument(request.params[2], UniValue::VARR);
UniValue inputs = request.params[2].get_array();
for (unsigned int idx = 0; idx < inputs.size(); idx++) {
const UniValue& input = inputs[idx];
@@ -2410,11 +2458,17 @@ UniValue listunspent(const JSONRPCRequest& request)
}
}
+ bool include_unsafe = true;
+ if (request.params.size() > 3 && !request.params[3].isNull()) {
+ RPCTypeCheckArgument(request.params[3], UniValue::VBOOL);
+ include_unsafe = request.params[3].get_bool();
+ }
+
UniValue results(UniValue::VARR);
vector<COutput> vecOutputs;
assert(pwalletMain != NULL);
LOCK2(cs_main, pwalletMain->cs_wallet);
- pwalletMain->AvailableCoins(vecOutputs, false, NULL, true);
+ pwalletMain->AvailableCoins(vecOutputs, !include_unsafe, NULL, true);
BOOST_FOREACH(const COutput& out, vecOutputs) {
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue;
@@ -2464,7 +2518,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
throw runtime_error(
"fundrawtransaction \"hexstring\" ( options )\n"
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
- "This will not modify existing inputs, and will add one change output to the outputs.\n"
+ "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
+ "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
"The inputs added will not be signed, use signrawtransaction for that.\n"
"Note that all existing inputs must have their previous output transaction be in the wallet.\n"
@@ -2474,13 +2529,20 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
"Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
"\nArguments:\n"
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
- "2. options (object, optional)\n"
+ "2. options (object, optional)\n"
" {\n"
- " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
- " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
- " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
- " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
- " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n"
+ " \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
+ " \"changePosition\" (numeric, optional, default random) The index of the change output\n"
+ " \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
+ " \"lockUnspents\" (boolean, optional, default false) Lock selected unspent outputs\n"
+ " \"reserveChangeKey\" (boolean, optional, default true) Reserves the change output key from the keypool\n"
+ " \"feeRate\" (numeric, optional, default not set: makes wallet determine the fee) Set a specific feerate (" + CURRENCY_UNIT + " per KB)\n"
+ " \"subtractFeeFromOutputs\" (array, optional) A json array of integers.\n"
+ " The fee will be equally deducted from the amount of each specified output.\n"
+ " The outputs are specified by their zero-based index, before any change output is added.\n"
+ " Those recipients will receive less bitcoins than you enter in their corresponding amount field.\n"
+ " If no outputs are specified here, the sender pays the fee.\n"
+ " [vout_index,...]\n"
" }\n"
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
"\nResult:\n"
@@ -2489,7 +2551,6 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
" \"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"
- "\"hex\" \n"
"\nExamples:\n"
"\nCreate a transaction with no inputs\n"
+ HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
@@ -2507,8 +2568,11 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
int changePosition = -1;
bool includeWatching = false;
bool lockUnspents = false;
+ bool reserveChangeKey = true;
CFeeRate feeRate = CFeeRate(0);
bool overrideEstimatedFeerate = false;
+ UniValue subtractFeeFromOutputs;
+ set<int> setSubtractFeeFromOutputs;
if (request.params.size() > 1) {
if (request.params[1].type() == UniValue::VBOOL) {
@@ -2526,7 +2590,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
{"changePosition", UniValueType(UniValue::VNUM)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
+ {"reserveChangeKey", UniValueType(UniValue::VBOOL)},
{"feeRate", UniValueType()}, // will be checked below
+ {"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
},
true, true);
@@ -2548,11 +2614,17 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (options.exists("lockUnspents"))
lockUnspents = options["lockUnspents"].get_bool();
+ if (options.exists("reserveChangeKey"))
+ reserveChangeKey = options["reserveChangeKey"].get_bool();
+
if (options.exists("feeRate"))
{
feeRate = CFeeRate(AmountFromValue(options["feeRate"]));
overrideEstimatedFeerate = true;
}
+
+ if (options.exists("subtractFeeFromOutputs"))
+ subtractFeeFromOutputs = options["subtractFeeFromOutputs"].get_array();
}
}
@@ -2567,10 +2639,21 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
if (changePosition != -1 && (changePosition < 0 || (unsigned int)changePosition > tx.vout.size()))
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
+ for (unsigned int idx = 0; idx < subtractFeeFromOutputs.size(); idx++) {
+ int pos = subtractFeeFromOutputs[idx].get_int();
+ if (setSubtractFeeFromOutputs.count(pos))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, duplicated position: %d", pos));
+ if (pos < 0)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, negative position: %d", pos));
+ if (pos >= int(tx.vout.size()))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, position too large: %d", pos));
+ setSubtractFeeFromOutputs.insert(pos);
+ }
+
CAmount nFeeOut;
string strFailReason;
- if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, changeAddress))
+ if(!pwalletMain->FundTransaction(tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition, strFailReason, includeWatching, lockUnspents, setSubtractFeeFromOutputs, reserveChangeKey, changeAddress))
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
UniValue result(UniValue::VOBJ);
@@ -2581,6 +2664,323 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return result;
}
+// Calculate the size of the transaction assuming all signatures are max size
+// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
+// TODO: re-use this in CWallet::CreateTransaction (right now
+// CreateTransaction uses the constructed dummy-signed tx to do a priority
+// calculation, but we should be able to refactor after priority is removed).
+// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+// be IsAllFromMe).
+int64_t CalculateMaximumSignedTxSize(const CTransaction &tx)
+{
+ CMutableTransaction txNew(tx);
+ std::vector<pair<CWalletTx *, unsigned int>> vCoins;
+ // Look up the inputs. We should have already checked that this transaction
+ // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
+ // wallet, with a valid index into the vout array.
+ for (auto& input : tx.vin) {
+ const auto mi = pwalletMain->mapWallet.find(input.prevout.hash);
+ assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
+ vCoins.emplace_back(make_pair(&(mi->second), input.prevout.n));
+ }
+ if (!pwalletMain->DummySignTx(txNew, vCoins)) {
+ // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
+ // implies that we can sign for every input.
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that cannot be signed");
+ }
+ return GetVirtualTransactionSize(txNew);
+}
+
+UniValue bumpfee(const JSONRPCRequest& request)
+{
+ if (!EnsureWalletIsAvailable(request.fHelp)) {
+ return NullUniValue;
+ }
+
+ if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) {
+ throw runtime_error(
+ "bumpfee \"txid\" ( options ) \n"
+ "\nBumps the fee of an opt-in-RBF transaction T, replacing it with a new transaction B.\n"
+ "An opt-in RBF transaction with the given txid must be in the wallet.\n"
+ "The command will pay the additional fee by decreasing (or perhaps removing) its change output.\n"
+ "If the change output is not big enough to cover the increased fee, the command will currently fail\n"
+ "instead of adding new inputs to compensate. (A future implementation could improve this.)\n"
+ "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
+ "By default, the new fee will be calculated automatically using estimatefee.\n"
+ "The user can specify a confirmation target for estimatefee.\n"
+ "Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate.\n"
+ "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
+ "returned by getnetworkinfo) to enter the node's mempool.\n"
+ "\nArguments:\n"
+ "1. txid (string, required) The txid to be bumped\n"
+ "2. options (object, optional)\n"
+ " {\n"
+ " \"confTarget\" (numeric, optional) Confirmation target (in blocks)\n"
+ " \"totalFee\" (numeric, optional) Total fee (NOT feerate) to pay, in satoshis.\n"
+ " In rare cases, the actual fee paid might be slightly higher than the specified\n"
+ " totalFee if the tx change output has to be removed because it is too close to\n"
+ " the dust threshold.\n"
+ " \"replaceable\" (boolean, optional, default true) Whether the new transaction should still be\n"
+ " marked bip-125 replaceable. If true, the sequence numbers in the transaction will\n"
+ " be left unchanged from the original. If false, any input sequence numbers in the\n"
+ " original transaction that were less than 0xfffffffe will be increased to 0xfffffffe\n"
+ " so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
+ " still be replacable in practice, for example if it has unconfirmed ancestors which\n"
+ " are replaceable).\n"
+ " }\n"
+ "\nResult:\n"
+ "{\n"
+ " \"txid\": \"value\", (string) The id of the new transaction\n"
+ " \"origfee\": n, (numeric) Fee of the replaced transaction\n"
+ " \"fee\": n, (numeric) Fee of the new transaction\n"
+ " \"errors\": [ str... ] (json array of strings) Errors encountered during processing (may be empty)\n"
+ "}\n"
+ "\nExamples:\n"
+ "\nBump the fee, get the new transaction\'s txid\n" +
+ HelpExampleCli("bumpfee", "<txid>"));
+ }
+
+ RPCTypeCheck(request.params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
+ uint256 hash;
+ hash.SetHex(request.params[0].get_str());
+
+ // retrieve the original tx from the wallet
+ LOCK2(cs_main, pwalletMain->cs_wallet);
+ EnsureWalletIsUnlocked();
+ if (!pwalletMain->mapWallet.count(hash)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id");
+ }
+ CWalletTx& wtx = pwalletMain->mapWallet[hash];
+
+ if (pwalletMain->HasWalletSpend(hash)) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the wallet");
+ }
+
+ {
+ LOCK(mempool.cs);
+ auto it = mempool.mapTx.find(hash);
+ if (it != mempool.mapTx.end() && it->GetCountWithDescendants() > 1) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Transaction has descendants in the mempool");
+ }
+ }
+
+ if (wtx.GetDepthInMainChain() != 0) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction has been mined, or is conflicted with a mined transaction");
+ }
+
+ if (!SignalsOptInRBF(wtx)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction is not BIP 125 replaceable");
+ }
+
+ if (wtx.mapValue.count("replaced_by_txid")) {
+ throw JSONRPCError(RPC_INVALID_REQUEST, strprintf("Cannot bump transaction %s which was already bumped by transaction %s", hash.ToString(), wtx.mapValue.at("replaced_by_txid")));
+ }
+
+ // 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 (!pwalletMain->IsAllFromMe(wtx, ISMINE_SPENDABLE)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction contains inputs that don't belong to this wallet");
+ }
+
+ // figure out which output was change
+ // if there was no change output or multiple change outputs, fail
+ int nOutput = -1;
+ for (size_t i = 0; i < wtx.tx->vout.size(); ++i) {
+ if (pwalletMain->IsChange(wtx.tx->vout[i])) {
+ if (nOutput != -1) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Transaction has multiple change outputs");
+ }
+ nOutput = i;
+ }
+ }
+ if (nOutput == -1) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Transaction does not have a change output");
+ }
+
+ // Calculate the expected size of the new transaction.
+ int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
+ const int64_t maxNewTxSize = CalculateMaximumSignedTxSize(*wtx.tx);
+
+ // optional parameters
+ bool specifiedConfirmTarget = false;
+ int newConfirmTarget = nTxConfirmTarget;
+ CAmount totalFee = 0;
+ bool replaceable = true;
+ if (request.params.size() > 1) {
+ UniValue options = request.params[1];
+ RPCTypeCheckObj(options,
+ {
+ {"confTarget", UniValueType(UniValue::VNUM)},
+ {"totalFee", UniValueType(UniValue::VNUM)},
+ {"replaceable", UniValueType(UniValue::VBOOL)},
+ },
+ true, true);
+
+ if (options.exists("confTarget") && options.exists("totalFee")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction.");
+ } else if (options.exists("confTarget")) {
+ specifiedConfirmTarget = true;
+ newConfirmTarget = options["confTarget"].get_int();
+ if (newConfirmTarget <= 0) { // upper-bound will be checked by estimatefee/smartfee
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid confTarget (cannot be <= 0)");
+ }
+ } else if (options.exists("totalFee")) {
+ totalFee = options["totalFee"].get_int64();
+ CAmount requiredFee = CWallet::GetRequiredFee(maxNewTxSize);
+ if (totalFee < requiredFee ) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER,
+ strprintf("Insufficient totalFee (cannot be less than required fee %s)",
+ FormatMoney(requiredFee)));
+ }
+ }
+
+ if (options.exists("replaceable")) {
+ replaceable = options["replaceable"].get_bool();
+ }
+ }
+
+ // calculate the old fee and fee-rate
+ CAmount nOldFee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+ CFeeRate nOldFeeRate(nOldFee, txSize);
+ CAmount nNewFee;
+ CFeeRate nNewFeeRate;
+ // The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
+ // future proof against changes to network wide policy for incremental relay
+ // fee that our node may not be aware of.
+ CFeeRate walletIncrementalRelayFee = CFeeRate(WALLET_INCREMENTAL_RELAY_FEE);
+ if (::incrementalRelayFee > walletIncrementalRelayFee) {
+ walletIncrementalRelayFee = ::incrementalRelayFee;
+ }
+
+ if (totalFee > 0) {
+ CAmount minTotalFee = nOldFeeRate.GetFee(maxNewTxSize) + ::incrementalRelayFee.GetFee(maxNewTxSize);
+ if (totalFee < minTotalFee) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s)",
+ FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxNewTxSize)), FormatMoney(::incrementalRelayFee.GetFee(maxNewTxSize))));
+ }
+ nNewFee = totalFee;
+ nNewFeeRate = CFeeRate(totalFee, maxNewTxSize);
+ } else {
+ // if user specified a confirm target then don't consider any global payTxFee
+ if (specifiedConfirmTarget) {
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool, CAmount(0));
+ }
+ // otherwise use the regular wallet logic to select payTxFee or default confirm target
+ else {
+ nNewFee = CWallet::GetMinimumFee(maxNewTxSize, newConfirmTarget, mempool);
+ }
+
+ nNewFeeRate = CFeeRate(nNewFee, maxNewTxSize);
+
+ // New fee rate must be at least old rate + minimum incremental relay rate
+ // walletIncrementalRelayFee.GetFeePerK() should be exact, because it's initialized
+ // in that unit (fee per kb).
+ // However, nOldFeeRate is a calculated value from the tx fee/size, so
+ // add 1 satoshi to the result, because it may have been rounded down.
+ if (nNewFeeRate.GetFeePerK() < nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK()) {
+ nNewFeeRate = CFeeRate(nOldFeeRate.GetFeePerK() + 1 + walletIncrementalRelayFee.GetFeePerK());
+ nNewFee = nNewFeeRate.GetFee(maxNewTxSize);
+ }
+ }
+
+ // Check that in all cases the new fee doesn't violate maxTxFee
+ if (nNewFee > maxTxFee) {
+ throw JSONRPCError(RPC_MISC_ERROR,
+ strprintf("Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s)",
+ FormatMoney(nNewFee), FormatMoney(maxTxFee)));
+ }
+
+ // check that fee rate is higher than mempool's minimum fee
+ // (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
+ // This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
+ // in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
+ // moment earlier. In this case, we report an error to the user, who may use totalFee to make an adjustment.
+ CFeeRate minMempoolFeeRate = mempool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
+ if (nNewFeeRate.GetFeePerK() < minMempoolFeeRate.GetFeePerK()) {
+ throw JSONRPCError(RPC_MISC_ERROR, strprintf("New fee rate (%s) is less than the minimum fee rate (%s) to get into the mempool. totalFee value should to be at least %s or settxfee value should be at least %s to add transaction.", FormatMoney(nNewFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFeePerK()), FormatMoney(minMempoolFeeRate.GetFee(maxNewTxSize)), FormatMoney(minMempoolFeeRate.GetFeePerK())));
+ }
+
+ // Now modify the output to increase the fee.
+ // If the output is not large enough to pay the fee, fail.
+ CAmount nDelta = nNewFee - nOldFee;
+ assert(nDelta > 0);
+ CMutableTransaction tx(*(wtx.tx));
+ CTxOut* poutput = &(tx.vout[nOutput]);
+ if (poutput->nValue < nDelta) {
+ throw JSONRPCError(RPC_MISC_ERROR, "Change output is too small to bump the fee");
+ }
+
+ // If the output would become dust, discard it (converting the dust to fee)
+ poutput->nValue -= nDelta;
+ if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
+ LogPrint("rpc", "Bumping fee and discarding dust output\n");
+ nNewFee += poutput->nValue;
+ tx.vout.erase(tx.vout.begin() + nOutput);
+ }
+
+ // Mark new tx not replaceable, if requested.
+ if (!replaceable) {
+ for (auto& input : tx.vin) {
+ if (input.nSequence < 0xfffffffe) input.nSequence = 0xfffffffe;
+ }
+ }
+
+ // sign the new tx
+ CTransaction txNewConst(tx);
+ int nIn = 0;
+ for (auto& input : tx.vin) {
+ std::map<uint256, CWalletTx>::const_iterator mi = pwalletMain->mapWallet.find(input.prevout.hash);
+ assert(mi != pwalletMain->mapWallet.end() && input.prevout.n < mi->second.tx->vout.size());
+ const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey;
+ const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
+ SignatureData sigdata;
+ if (!ProduceSignature(TransactionSignatureCreator(pwalletMain, &txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Can't sign transaction.");
+ }
+ UpdateTransaction(tx, nIn, sigdata);
+ nIn++;
+ }
+
+ // commit/broadcast the tx
+ CReserveKey reservekey(pwalletMain);
+ CWalletTx wtxBumped(pwalletMain, MakeTransactionRef(std::move(tx)));
+ wtxBumped.mapValue = wtx.mapValue;
+ wtxBumped.mapValue["replaces_txid"] = hash.ToString();
+ wtxBumped.vOrderForm = wtx.vOrderForm;
+ wtxBumped.strFromAccount = wtx.strFromAccount;
+ wtxBumped.fTimeReceivedIsTxTime = true;
+ wtxBumped.fFromMe = true;
+ CValidationState state;
+ if (!pwalletMain->CommitTransaction(wtxBumped, reservekey, g_connman.get(), state)) {
+ // NOTE: CommitTransaction never returns false, so this should never happen.
+ throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error: The transaction was rejected! Reason given: %s", state.GetRejectReason()));
+ }
+
+ UniValue vErrors(UniValue::VARR);
+ if (state.IsInvalid()) {
+ // This can happen if the mempool rejected the transaction. Report
+ // what happened in the "errors" response.
+ vErrors.push_back(strprintf("Error: The transaction was rejected: %s", FormatStateMessage(state)));
+ }
+
+ // mark the original tx as bumped
+ if (!pwalletMain->MarkReplaced(wtx.GetHash(), wtxBumped.GetHash())) {
+ // TODO: see if JSON-RPC has a standard way of returning a response
+ // along with an exception. It would be good to return information about
+ // wtxBumped to the caller even if marking the original transaction
+ // replaced does not succeed for some reason.
+ vErrors.push_back("Error: Created new bumpfee transaction but could not mark the original transaction as replaced.");
+ }
+
+ UniValue result(UniValue::VOBJ);
+ result.push_back(Pair("txid", wtxBumped.GetHash().GetHex()));
+ result.push_back(Pair("origfee", ValueFromAmount(nOldFee)));
+ result.push_back(Pair("fee", ValueFromAmount(nNewFee)));
+ result.push_back(Pair("errors", vErrors));
+
+ return result;
+}
+
extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp
extern UniValue importprivkey(const JSONRPCRequest& request);
extern UniValue importaddress(const JSONRPCRequest& request);
@@ -2594,53 +2994,54 @@ extern UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) okSafeMode
// --------------------- ------------------------ ----------------------- ----------
- { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false },
- { "hidden", "resendwallettransactions", &resendwallettransactions, true },
- { "wallet", "abandontransaction", &abandontransaction, false },
- { "wallet", "addmultisigaddress", &addmultisigaddress, true },
- { "wallet", "addwitnessaddress", &addwitnessaddress, true },
- { "wallet", "backupwallet", &backupwallet, true },
- { "wallet", "dumpprivkey", &dumpprivkey, true },
- { "wallet", "dumpwallet", &dumpwallet, true },
- { "wallet", "encryptwallet", &encryptwallet, true },
- { "wallet", "getaccountaddress", &getaccountaddress, true },
- { "wallet", "getaccount", &getaccount, true },
- { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true },
- { "wallet", "getbalance", &getbalance, false },
- { "wallet", "getnewaddress", &getnewaddress, true },
- { "wallet", "getrawchangeaddress", &getrawchangeaddress, true },
- { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false },
- { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false },
- { "wallet", "gettransaction", &gettransaction, false },
- { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false },
- { "wallet", "getwalletinfo", &getwalletinfo, false },
- { "wallet", "importmulti", &importmulti, true },
- { "wallet", "importprivkey", &importprivkey, true },
- { "wallet", "importwallet", &importwallet, true },
- { "wallet", "importaddress", &importaddress, true },
- { "wallet", "importprunedfunds", &importprunedfunds, true },
- { "wallet", "importpubkey", &importpubkey, true },
- { "wallet", "keypoolrefill", &keypoolrefill, true },
- { "wallet", "listaccounts", &listaccounts, false },
- { "wallet", "listaddressgroupings", &listaddressgroupings, false },
- { "wallet", "listlockunspent", &listlockunspent, false },
- { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false },
- { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false },
- { "wallet", "listsinceblock", &listsinceblock, false },
- { "wallet", "listtransactions", &listtransactions, false },
- { "wallet", "listunspent", &listunspent, false },
- { "wallet", "lockunspent", &lockunspent, true },
- { "wallet", "move", &movecmd, false },
- { "wallet", "sendfrom", &sendfrom, false },
- { "wallet", "sendmany", &sendmany, false },
- { "wallet", "sendtoaddress", &sendtoaddress, false },
- { "wallet", "setaccount", &setaccount, true },
- { "wallet", "settxfee", &settxfee, true },
- { "wallet", "signmessage", &signmessage, true },
- { "wallet", "walletlock", &walletlock, true },
- { "wallet", "walletpassphrasechange", &walletpassphrasechange, true },
- { "wallet", "walletpassphrase", &walletpassphrase, true },
- { "wallet", "removeprunedfunds", &removeprunedfunds, true },
+ { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false, {"hexstring","options"} },
+ { "hidden", "resendwallettransactions", &resendwallettransactions, true, {} },
+ { "wallet", "abandontransaction", &abandontransaction, false, {"txid"} },
+ { "wallet", "addmultisigaddress", &addmultisigaddress, true, {"nrequired","keys","account"} },
+ { "wallet", "addwitnessaddress", &addwitnessaddress, true, {"address"} },
+ { "wallet", "backupwallet", &backupwallet, true, {"destination"} },
+ { "wallet", "bumpfee", &bumpfee, true, {"txid", "options"} },
+ { "wallet", "dumpprivkey", &dumpprivkey, true, {"address"} },
+ { "wallet", "dumpwallet", &dumpwallet, true, {"filename"} },
+ { "wallet", "encryptwallet", &encryptwallet, true, {"passphrase"} },
+ { "wallet", "getaccountaddress", &getaccountaddress, true, {"account"} },
+ { "wallet", "getaccount", &getaccount, true, {"address"} },
+ { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, {"account"} },
+ { "wallet", "getbalance", &getbalance, false, {"account","minconf","include_watchonly"} },
+ { "wallet", "getnewaddress", &getnewaddress, true, {"account"} },
+ { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, {} },
+ { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, {"account","minconf"} },
+ { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, {"address","minconf"} },
+ { "wallet", "gettransaction", &gettransaction, false, {"txid","include_watchonly"} },
+ { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, {} },
+ { "wallet", "getwalletinfo", &getwalletinfo, false, {} },
+ { "wallet", "importmulti", &importmulti, true, {"requests","options"} },
+ { "wallet", "importprivkey", &importprivkey, true, {"privkey","label","rescan"} },
+ { "wallet", "importwallet", &importwallet, true, {"filename"} },
+ { "wallet", "importaddress", &importaddress, true, {"address","label","rescan","p2sh"} },
+ { "wallet", "importprunedfunds", &importprunedfunds, true, {"rawtransaction","txoutproof"} },
+ { "wallet", "importpubkey", &importpubkey, true, {"pubkey","label","rescan"} },
+ { "wallet", "keypoolrefill", &keypoolrefill, true, {"newsize"} },
+ { "wallet", "listaccounts", &listaccounts, false, {"minconf","include_watchonly"} },
+ { "wallet", "listaddressgroupings", &listaddressgroupings, false, {} },
+ { "wallet", "listlockunspent", &listlockunspent, false, {} },
+ { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, {"minconf","include_empty","include_watchonly"} },
+ { "wallet", "listsinceblock", &listsinceblock, false, {"blockhash","target_confirmations","include_watchonly"} },
+ { "wallet", "listtransactions", &listtransactions, false, {"account","count","skip","include_watchonly"} },
+ { "wallet", "listunspent", &listunspent, false, {"minconf","maxconf","addresses","include_unsafe"} },
+ { "wallet", "lockunspent", &lockunspent, true, {"unlock","transactions"} },
+ { "wallet", "move", &movecmd, false, {"fromaccount","toaccount","amount","minconf","comment"} },
+ { "wallet", "sendfrom", &sendfrom, false, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
+ { "wallet", "sendmany", &sendmany, false, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
+ { "wallet", "sendtoaddress", &sendtoaddress, false, {"address","amount","comment","comment_to","subtractfeefromamount"} },
+ { "wallet", "setaccount", &setaccount, true, {"address","account"} },
+ { "wallet", "settxfee", &settxfee, true, {"amount"} },
+ { "wallet", "signmessage", &signmessage, true, {"address","message"} },
+ { "wallet", "walletlock", &walletlock, true, {} },
+ { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, {"oldpassphrase","newpassphrase"} },
+ { "wallet", "walletpassphrase", &walletpassphrase, true, {"passphrase","timeout"} },
+ { "wallet", "removeprunedfunds", &removeprunedfunds, true, {"txid"} },
};
void RegisterWalletRPCCommands(CRPCTable &t)
diff --git a/src/wallet/test/accounting_tests.cpp b/src/wallet/test/accounting_tests.cpp
index eb14d176bd..1fe633f2e5 100644
--- a/src/wallet/test/accounting_tests.cpp
+++ b/src/wallet/test/accounting_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2015 The Bitcoin Core developers
+// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/crypto_tests.cpp b/src/wallet/test/crypto_tests.cpp
index c64c76244e..0d012dacad 100644
--- a/src/wallet/test/crypto_tests.cpp
+++ b/src/wallet/test/crypto_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2014 The Bitcoin Core developers
+// Copyright (c) 2014-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 8085706a99..ca086c86a8 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2015 The Bitcoin Core developers
+// Copyright (c) 2012-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -23,6 +23,8 @@
using namespace std;
+std::vector<std::unique_ptr<CWalletTx>> wtxn;
+
typedef set<pair<const CWalletTx*,unsigned int> > CoinSet;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
@@ -42,21 +44,21 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- CWalletTx* wtx = new CWalletTx(&wallet, MakeTransactionRef(std::move(tx)));
+ std::unique_ptr<CWalletTx> wtx(new CWalletTx(&wallet, MakeTransactionRef(std::move(tx))));
if (fIsFromMe)
{
wtx->fDebitCached = true;
wtx->nDebitCached = 1;
}
- COutput output(wtx, nInput, nAge, true, true);
+ COutput output(wtx.get(), nInput, nAge, true, true);
vCoins.push_back(output);
+ wtxn.emplace_back(std::move(wtx));
}
static void empty_wallet(void)
{
- BOOST_FOREACH(COutput output, vCoins)
- delete output.tx;
vCoins.clear();
+ wtxn.clear();
}
static bool equal_sets(CoinSet a, CoinSet b)
@@ -78,24 +80,24 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
empty_wallet();
// with an empty wallet we can't even pay one cent
- BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
add_coin(1*CENT, 4); // add a new 1 cent coin
// with a new 1 cent coin, we still can't find a mature 1 cent
- BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can find a new 1 cent
- BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
add_coin(2*CENT); // add a mature 2 cent coin
// we can't make 3 cents of mature coins
- BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can make 3 cents of new coins
- BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
add_coin(5*CENT); // add a mature 5 cent coin,
@@ -105,33 +107,33 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
// we can't make 38 cents only if we disallow new coins:
- BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
// we can't even make 37 cents if we don't allow new coins even if they're from us
- BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, 0, vCoins, setCoinsRet, nValueRet));
// but we can make 37 cents if we accept new coins from ourself
- BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
// and we can make 38 cents if we accept all new coins
- BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
- BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
- BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
- BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK(nValueRet == 8 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
- BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -145,30 +147,30 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total
// check that we have 71 and not 72
- BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
- BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
// now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20
- BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total
// now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20
- BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30
// and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18
- BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins
// now try making 11 cents. we should get 5+6
- BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -177,11 +179,11 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin( 2*COIN);
add_coin( 3*COIN);
add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents
- BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
- BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -196,14 +198,14 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
- BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
// but if we add a bigger coin, small change is avoided
add_coin(1111*MIN_CHANGE);
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
- BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// if we add more small coins:
@@ -211,7 +213,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 7 / 10);
// and try again to make 1.0 * MIN_CHANGE
- BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
@@ -220,7 +222,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
for (int j = 0; j < 20; j++)
add_coin(50000 * COIN);
- BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
@@ -233,7 +235,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 7 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
@@ -243,7 +245,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 6 / 10);
add_coin(MIN_CHANGE * 8 / 10);
add_coin(1111 * MIN_CHANGE);
- BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
@@ -254,12 +256,12 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
add_coin(MIN_CHANGE * 100);
// trying to make 100.01 from these three coins
- BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 10001 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE * 10105 / 100); // we should get all coins
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
- BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(MIN_CHANGE * 9990 / 100, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
@@ -269,7 +271,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// Create 676 inputs (= (old MAX_STANDARD_TX_SIZE == 100000) / 148 bytes per input)
for (uint16_t j = 0; j < 676; j++)
add_coin(amt);
- BOOST_CHECK(wallet.SelectCoinsMinConf(2000, 1, 1, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(2000, 1, 1, 0, vCoins, setCoinsRet, nValueRet));
if (amt - 2000 < MIN_CHANGE) {
// needs more than one input:
uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
@@ -291,8 +293,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
// picking 50 from 100 coins doesn't depend on the shuffle,
// but does depend on randomness in the stochastic approximation code
- BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet , nValueRet));
- BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, vCoins, setCoinsRet2, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(50 * COIN, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
BOOST_CHECK(!equal_sets(setCoinsRet, setCoinsRet2));
int fails = 0;
@@ -300,8 +302,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
// run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet , nValueRet));
- BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, vCoins, setCoinsRet2, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(COIN, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -321,8 +323,8 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
{
// selecting 1 from 100 identical coins depends on the shuffle; this test will fail 1% of the time
// run the test RANDOM_REPEATS times and only complain if all of them fail
- BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet , nValueRet));
- BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, vCoins, setCoinsRet2, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, 0, vCoins, setCoinsRet , nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(90*CENT, 1, 6, 0, vCoins, setCoinsRet2, nValueRet));
if (equal_sets(setCoinsRet, setCoinsRet2))
fails++;
}
@@ -346,9 +348,11 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
add_coin(1000 * COIN);
add_coin(3 * COIN);
- BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, vCoins, setCoinsRet, nValueRet));
+ BOOST_CHECK(wallet.SelectCoinsMinConf(1003 * COIN, 1, 6, 0, vCoins, setCoinsRet, nValueRet));
BOOST_CHECK_EQUAL(nValueRet, 1003 * COIN);
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
+
+ empty_wallet();
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4c6916c04a..a7b8022bd9 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -411,6 +411,13 @@ set<uint256> CWallet::GetConflicts(const uint256& txid) const
return result;
}
+bool CWallet::HasWalletSpend(const uint256& txid) const
+{
+ AssertLockHeld(cs_wallet);
+ auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));
+ return (iter != mapTxSpends.end() && iter->first.hash == txid);
+}
+
void CWallet::Flush(bool shutdown)
{
bitdb.Flush(shutdown);
@@ -826,6 +833,35 @@ void CWallet::MarkDirty()
}
}
+bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
+{
+ LOCK(cs_wallet);
+
+ auto mi = mapWallet.find(originalHash);
+
+ // There is a bug if MarkReplaced is not called on an existing wallet transaction.
+ assert(mi != mapWallet.end());
+
+ CWalletTx& wtx = (*mi).second;
+
+ // Ensure for now that we're not overwriting data
+ assert(wtx.mapValue.count("replaced_by_txid") == 0);
+
+ wtx.mapValue["replaced_by_txid"] = newHash.ToString();
+
+ CWalletDB walletdb(strWalletFile, "r+");
+
+ bool success = true;
+ if (!walletdb.WriteTx(wtx)) {
+ LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString());
+ success = false;
+ }
+
+ NotifyTransactionChanged(this, originalHash, CT_UPDATED);
+
+ return success;
+}
+
bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
{
LOCK(cs_wallet);
@@ -967,9 +1003,17 @@ bool CWallet::LoadToWallet(const CWalletTx& wtxIn)
}
/**
- * Add a transaction to the wallet, or update it.
- * pblock is optional, but should be provided if the transaction is known to be in a block.
+ * Add a transaction to the wallet, or update it. pIndex and posInBlock should
+ * be set when the transaction was known to be included in a block. When
+ * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not
+ * updated in AddToWallet, but notifications happen and cached balances are
+ * marked dirty.
* If fUpdate is true, existing transactions will be updated.
+ * TODO: One exception to this is that the abandoned state is cleared under the
+ * assumption that any further notification of a transaction that was considered
+ * abandoned is an indication that it is not safe to be considered abandoned.
+ * Abandoned state should probably be more carefuly tracked via different
+ * posInBlock signals or by checking mempool presence when necessary.
*/
bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)
{
@@ -1009,8 +1053,7 @@ bool CWallet::AbandonTransaction(const uint256& hashTx)
{
LOCK2(cs_main, cs_wallet);
- // Do not flush the wallet here for performance reasons
- CWalletDB walletdb(strWalletFile, "r+", false);
+ CWalletDB walletdb(strWalletFile, "r+");
std::set<uint256> todo;
std::set<uint256> done;
@@ -1155,6 +1198,8 @@ isminetype CWallet::IsMine(const CTxIn &txin) const
return ISMINE_NO;
}
+// Note that this function doesn't distinguish between a 0-valued input,
+// and a not-"is mine" (according to the filter) input.
CAmount CWallet::GetDebit(const CTxIn &txin, const isminefilter& filter) const
{
{
@@ -1237,6 +1282,27 @@ CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) co
return nDebit;
}
+bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const
+{
+ LOCK(cs_wallet);
+
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ auto mi = mapWallet.find(txin.prevout.hash);
+ if (mi == mapWallet.end())
+ return false; // any unknown inputs can't be from us
+
+ const CWalletTx& prev = (*mi).second;
+
+ if (txin.prevout.n >= prev.tx->vout.size())
+ return false; // invalid input!
+
+ if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))
+ return false;
+ }
+ return true;
+}
+
CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const
{
CAmount nCredit = 0;
@@ -1480,12 +1546,12 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
pindex = chainActive.Next(pindex);
ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup
- double dProgressStart = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false);
- double dProgressTip = Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), chainActive.Tip(), false);
+ double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);
+ double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
while (pindex)
{
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)
- ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex, false) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
+ ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));
CBlock block;
ReadBlockFromDisk(block, pindex, Params().GetConsensus());
@@ -1498,7 +1564,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) {
nNow = GetTime();
- LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, Checkpoints::GuessVerificationProgress(chainParams.Checkpoints(), pindex));
+ LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));
}
}
ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI
@@ -1959,6 +2025,37 @@ void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const
if (nDepth == 0 && !pcoin->InMempool())
continue;
+ // We should not consider coins from transactions that are replacing
+ // other transactions.
+ //
+ // Example: There is a transaction A which is replaced by bumpfee
+ // transaction B. In this case, we want to prevent creation of
+ // a transaction B' which spends an output of B.
+ //
+ // Reason: If transaction A were initially confirmed, transactions B
+ // and B' would no longer be valid, so the user would have to create
+ // a new transaction C to replace B'. However, in the case of a
+ // one-block reorg, transactions B' and C might BOTH be accepted,
+ // when the user only wanted one of them. Specifically, there could
+ // be a 1-block reorg away from the chain where transactions A and C
+ // were accepted to another chain where B, B', and C were all
+ // accepted.
+ if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) {
+ continue;
+ }
+
+ // Similarly, we should not consider coins from transactions that
+ // have been replaced. In the example above, we would want to prevent
+ // creation of a transaction A' spending an output of A, because if
+ // transaction B were initially confirmed, conflicting with A and
+ // A', we wouldn't want to the user to create a transaction D
+ // intending to replace A', but potentially resulting in a scenario
+ // where A, A', and D could all be accepted (instead of just B and
+ // D, or just A and A' like the user would want).
+ if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) {
+ continue;
+ }
+
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {
isminetype mine = IsMine(pcoin->tx->vout[i]);
if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&
@@ -2019,7 +2116,7 @@ static void ApproximateBestSubset(vector<pair<CAmount, pair<const CWalletTx*,uns
}
}
-bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
+bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, vector<COutput> vCoins,
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const
{
setCoinsRet.clear();
@@ -2044,6 +2141,9 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
if (output.nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs))
continue;
+ if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors))
+ continue;
+
int i = output.i;
CAmount n = pcoin->tx->vout[i].nValue;
@@ -2169,10 +2269,17 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
++it;
}
+ size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
+ bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
+
bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, vCoins, setCoinsRet, nValueRet) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, vCoins, setCoinsRet, nValueRet) ||
- (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, vCoins, setCoinsRet, nValueRet));
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet) ||
+ SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet)) ||
+ (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits<uint64_t>::max(), vCoins, setCoinsRet, nValueRet));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());
@@ -2183,14 +2290,15 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
return res;
}
-bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange)
+bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)
{
vector<CRecipient> vecSend;
// Turn the txout set into a CRecipient vector
- BOOST_FOREACH(const CTxOut& txOut, tx.vout)
+ for (size_t idx = 0; idx < tx.vout.size(); idx++)
{
- CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, false};
+ const CTxOut& txOut = tx.vout[idx];
+ CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};
vecSend.push_back(recipient);
}
@@ -2212,6 +2320,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
if (nChangePosInOut != -1)
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);
+ // Copy output sizes from new transaction; they may have had the fee subtracted from them
+ for (unsigned int idx = 0; idx < tx.vout.size(); idx++)
+ tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;
+
// Add new txins (keeping original txin scriptSig/order)
BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)
{
@@ -2227,6 +2339,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov
}
}
+ // optionally keep the change output key
+ if (keepReserveKey)
+ reservekey.KeepKey();
+
return true;
}
@@ -2236,7 +2352,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
CAmount nValue = 0;
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
- BOOST_FOREACH (const CRecipient& recipient, vecSend)
+ for (const auto& recipient : vecSend)
{
if (nValue < 0 || recipient.nAmount < 0)
{
@@ -2291,6 +2407,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
+ set<pair<const CWalletTx*,unsigned int> > setCoins;
LOCK2(cs_main, cs_wallet);
{
std::vector<COutput> vAvailableCoins;
@@ -2303,7 +2420,6 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nChangePosInOut = nChangePosRequest;
txNew.vin.clear();
txNew.vout.clear();
- txNew.wit.SetNull();
wtxNew.fFromMe = true;
bool fFirst = true;
@@ -2312,7 +2428,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
nValueToSelect += nFeeRet;
double dPriority = 0;
// vouts to the payees
- BOOST_FOREACH (const CRecipient& recipient, vecSend)
+ for (const auto& recipient : vecSend)
{
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
@@ -2327,7 +2443,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
}
- if (txout.IsDust(::minRelayTxFee))
+ if (txout.IsDust(dustRelayFee))
{
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
{
@@ -2344,14 +2460,14 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
}
// Choose coins to use
- set<pair<const CWalletTx*,unsigned int> > setCoins;
CAmount nValueIn = 0;
+ setCoins.clear();
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))
{
strFailReason = _("Insufficient funds");
return false;
}
- BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ for (const auto& pcoin : setCoins)
{
CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;
//The coin age after the next block (depth+1) is used instead of the current,
@@ -2405,16 +2521,16 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// We do not move dust-change to fees, because the sender would end up paying more than requested.
// This would be against the purpose of the all-inclusive feature.
// So instead we raise the change and deduct from the recipient.
- if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))
+ if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))
{
- CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;
+ CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;
newTxOut.nValue += nDust; // raise change until no more dust
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
{
if (vecSend[i].fSubtractFeeFromAmount)
{
txNew.vout[i].nValue -= nDust;
- if (txNew.vout[i].IsDust(::minRelayTxFee))
+ if (txNew.vout[i].IsDust(dustRelayFee))
{
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
return false;
@@ -2426,7 +2542,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (newTxOut.IsDust(::minRelayTxFee))
+ if (newTxOut.IsDust(dustRelayFee))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2460,57 +2576,29 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// BIP125 defines opt-in RBF as any nSequence < maxint-1, so
// we use the highest possible value in that range (maxint-2)
// to avoid conflicting with other possible uses of nSequence,
- // and in the spirit of "smallest posible change from prior
+ // and in the spirit of "smallest possible change from prior
// behavior."
- BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
+ for (const auto& coin : setCoins)
txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),
std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1)));
- // Sign
- int nIn = 0;
- CTransaction txNewConst(txNew);
- BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)
- {
- bool signSuccess;
- const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
- SignatureData sigdata;
- if (sign)
- signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);
- else
- signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);
-
- if (!signSuccess)
- {
- strFailReason = _("Signing transaction failed");
- return false;
- } else {
- UpdateTransaction(txNew, nIn, sigdata);
- }
-
- nIn++;
+ // Fill in dummy signatures for fee calculation.
+ if (!DummySignTx(txNew, setCoins)) {
+ strFailReason = _("Signing transaction failed");
+ return false;
}
unsigned int nBytes = GetVirtualTransactionSize(txNew);
- // Remove scriptSigs if we used dummy signatures for fee calculation
- if (!sign) {
- BOOST_FOREACH (CTxIn& vin, txNew.vin)
- vin.scriptSig = CScript();
- txNew.wit.SetNull();
- }
-
- // Embed the constructed transaction data in wtxNew.
- wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
+ CTransaction txNewConst(txNew);
+ dPriority = txNewConst.ComputePriority(dPriority, nBytes);
- // Limit size
- if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
- {
- strFailReason = _("Transaction too large");
- return false;
+ // Remove scriptSigs to eliminate the fee calculation dummy signatures
+ for (auto& vin : txNew.vin) {
+ vin.scriptSig = CScript();
+ vin.scriptWitness.SetNull();
}
- dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes);
-
// Allow to override the default confirmation target over the CoinControl instance
int currentConfirmationTarget = nTxConfirmTarget;
if (coinControl && coinControl->nConfirmTarget > 0)
@@ -2541,16 +2629,91 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
return false;
}
- if (nFeeRet >= nFeeNeeded)
+ if (nFeeRet >= nFeeNeeded) {
+ // Reduce fee to only the needed amount if we have change
+ // output to increase. This prevents potential overpayment
+ // in fees if the coins selected to meet nFeeNeeded result
+ // in a transaction that requires less fee than the prior
+ // iteration.
+ // TODO: The case where nSubtractFeeFromAmount > 0 remains
+ // to be addressed because it requires returning the fee to
+ // the payees and not the change output.
+ // TODO: The case where there is no change output remains
+ // to be addressed so we avoid creating too small an output.
+ if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
+ CAmount extraFeePaid = nFeeRet - nFeeNeeded;
+ vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
+ change_position->nValue += extraFeePaid;
+ nFeeRet -= extraFeePaid;
+ }
break; // Done, enough fee included.
+ }
+
+ // Try to reduce change to include necessary fee
+ if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
+ CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;
+ vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;
+ // Only reduce change if remaining amount is still a large enough output.
+ if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {
+ change_position->nValue -= additionalFeeNeeded;
+ nFeeRet += additionalFeeNeeded;
+ break; // Done, able to increase fee from change
+ }
+ }
// Include more fee and try again.
nFeeRet = nFeeNeeded;
continue;
}
}
+
+ if (sign)
+ {
+ CTransaction txNewConst(txNew);
+ int nIn = 0;
+ for (const auto& coin : setCoins)
+ {
+ const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ SignatureData sigdata;
+
+ if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))
+ {
+ strFailReason = _("Signing transaction failed");
+ return false;
+ } else {
+ UpdateTransaction(txNew, nIn, sigdata);
+ }
+
+ nIn++;
+ }
+ }
+
+ // Embed the constructed transaction data in wtxNew.
+ wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
+
+ // Limit size
+ if (GetTransactionWeight(wtxNew) >= MAX_STANDARD_TX_WEIGHT)
+ {
+ strFailReason = _("Transaction too large");
+ return false;
+ }
}
+ if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
+ // Lastly, ensure this tx will pass the mempool's chain limits
+ LockPoints lp;
+ CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, false, 0, lp);
+ CTxMemPool::setEntries setAncestors;
+ size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
+ size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
+ size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
+ size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
+ std::string errString;
+ if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
+ strFailReason = _("Transaction has too long of a mempool chain");
+ return false;
+ }
+ }
return true;
}
@@ -2627,8 +2790,13 @@ CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
{
- // payTxFee is user-set "I want to pay this much"
- CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
+ // payTxFee is the user-set global for desired feerate
+ return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));
+}
+
+CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)
+{
+ CAmount nFeeNeeded = targetFee;
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0) {
int estimateFoundTarget = nConfirmTarget;
@@ -3375,21 +3543,14 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE));
strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET));
strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB));
+ strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS));
}
return strUsage;
}
-bool CWallet::InitLoadWallet()
+CWallet* CWallet::CreateWalletFromFile(const std::string walletFile)
{
- if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
- pwalletMain = NULL;
- LogPrintf("Wallet disabled!\n");
- return true;
- }
-
- std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
-
// needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
@@ -3399,7 +3560,8 @@ bool CWallet::InitLoadWallet()
CWallet *tempWallet = new CWallet(walletFile);
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
- return InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ return NULL;
}
delete tempWallet;
@@ -3414,23 +3576,29 @@ bool CWallet::InitLoadWallet()
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK)
{
- if (nLoadWalletRet == DB_CORRUPT)
- return InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ if (nLoadWalletRet == DB_CORRUPT) {
+ InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
+ return NULL;
+ }
else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)
{
InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect."),
walletFile));
}
- else if (nLoadWalletRet == DB_TOO_NEW)
- return InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"),
- walletFile, _(PACKAGE_NAME)));
+ else if (nLoadWalletRet == DB_TOO_NEW) {
+ InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
+ return NULL;
+ }
else if (nLoadWalletRet == DB_NEED_REWRITE)
{
- return InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
+ InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
+ return NULL;
+ }
+ else {
+ InitError(strprintf(_("Error loading %s"), walletFile));
+ return NULL;
}
- else
- return InitError(strprintf(_("Error loading %s"), walletFile));
}
if (GetBoolArg("-upgradewallet", fFirstRun))
@@ -3446,7 +3614,8 @@ bool CWallet::InitLoadWallet()
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
- return InitError(_("Cannot downgrade wallet"));
+ InitError(_("Cannot downgrade wallet"));
+ return NULL;
}
walletInstance->SetMaxVersion(nMaxVersion);
}
@@ -3463,18 +3632,24 @@ bool CWallet::InitLoadWallet()
CPubKey newDefaultKey;
if (walletInstance->GetKeyFromPool(newDefaultKey)) {
walletInstance->SetDefaultKey(newDefaultKey);
- if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive"))
- return InitError(_("Cannot write default address") += "\n");
+ if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) {
+ InitError(_("Cannot write default address") += "\n");
+ return NULL;
+ }
}
walletInstance->SetBestChain(chainActive.GetLocator());
}
- else if (mapArgs.count("-usehd")) {
+ else if (IsArgSet("-usehd")) {
bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
- if (walletInstance->IsHDEnabled() && !useHD)
- return InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile));
- if (!walletInstance->IsHDEnabled() && useHD)
- return InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile));
+ if (walletInstance->IsHDEnabled() && !useHD) {
+ InitError(strprintf(_("Error loading %s: You can't disable HD on a already existing HD wallet"), walletFile));
+ return NULL;
+ }
+ if (!walletInstance->IsHDEnabled() && useHD) {
+ InitError(strprintf(_("Error loading %s: You can't enable HD on a already existing non-HD wallet"), walletFile));
+ return NULL;
+ }
}
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart);
@@ -3504,8 +3679,10 @@ bool CWallet::InitLoadWallet()
while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block)
block = block->pprev;
- if (pindexRescan != block)
- return InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ if (pindexRescan != block) {
+ InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
+ return NULL;
+ }
}
uiInterface.InitMessage(_("Rescanning..."));
@@ -3550,11 +3727,30 @@ bool CWallet::InitLoadWallet()
LogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
}
- pwalletMain = walletInstance;
+ return walletInstance;
+}
+
+bool CWallet::InitLoadWallet()
+{
+ if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {
+ pwalletMain = NULL;
+ LogPrintf("Wallet disabled!\n");
+ return true;
+ }
+
+ std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);
+
+ CWallet * const pwallet = CreateWalletFromFile(walletFile);
+ if (!pwallet) {
+ return false;
+ }
+ pwalletMain = pwallet;
return true;
}
+std::atomic<bool> CWallet::fFlushThreadRunning(false);
+
void CWallet::postInitProcess(boost::thread_group& threadGroup)
{
// Add wallet transactions that aren't already in a block to mempool
@@ -3562,7 +3758,9 @@ void CWallet::postInitProcess(boost::thread_group& threadGroup)
ReacceptWalletTransactions();
// Run a thread to flush wallet periodically
- threadGroup.create_thread(boost::bind(&ThreadFlushWalletDB, boost::ref(this->strWalletFile)));
+ if (!CWallet::fFlushThreadRunning.exchange(true)) {
+ threadGroup.create_thread(ThreadFlushWalletDB);
+ }
}
bool CWallet::ParameterInteraction()
@@ -3593,31 +3791,31 @@ bool CWallet::ParameterInteraction()
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
_("The wallet will avoid paying less than the minimum relay fee."));
- if (mapArgs.count("-mintxfee"))
+ if (IsArgSet("-mintxfee"))
{
CAmount n = 0;
- if (!ParseMoney(mapArgs["-mintxfee"], n) || 0 == n)
- return InitError(AmountErrMsg("mintxfee", mapArgs["-mintxfee"]));
+ if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n)
+ return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", "")));
if (n > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-mintxfee") + " " +
_("This is the minimum transaction fee you pay on every transaction."));
CWallet::minTxFee = CFeeRate(n);
}
- if (mapArgs.count("-fallbackfee"))
+ if (IsArgSet("-fallbackfee"))
{
CAmount nFeePerK = 0;
- if (!ParseMoney(mapArgs["-fallbackfee"], nFeePerK))
- return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), mapArgs["-fallbackfee"]));
+ if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK))
+ return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), GetArg("-fallbackfee", "")));
if (nFeePerK > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-fallbackfee") + " " +
_("This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
}
- if (mapArgs.count("-paytxfee"))
+ if (IsArgSet("-paytxfee"))
{
CAmount nFeePerK = 0;
- if (!ParseMoney(mapArgs["-paytxfee"], nFeePerK))
- return InitError(AmountErrMsg("paytxfee", mapArgs["-paytxfee"]));
+ if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK))
+ return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", "")));
if (nFeePerK > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-paytxfee") + " " +
_("This is the transaction fee you will pay if you send a transaction."));
@@ -3626,21 +3824,21 @@ bool CWallet::ParameterInteraction()
if (payTxFee < ::minRelayTxFee)
{
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
- mapArgs["-paytxfee"], ::minRelayTxFee.ToString()));
+ GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
}
}
- if (mapArgs.count("-maxtxfee"))
+ if (IsArgSet("-maxtxfee"))
{
CAmount nMaxFee = 0;
- if (!ParseMoney(mapArgs["-maxtxfee"], nMaxFee))
- return InitError(AmountErrMsg("maxtxfee", mapArgs["-maxtxfee"]));
+ if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee))
+ return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", "")));
if (nMaxFee > HIGH_MAX_TX_FEE)
InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
maxTxFee = nMaxFee;
if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
{
return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
- mapArgs["-maxtxfee"], ::minRelayTxFee.ToString()));
+ GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
}
}
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);
@@ -3648,6 +3846,9 @@ bool CWallet::ParameterInteraction()
fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);
fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);
+ if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)
+ return InitError("Creation of free transactions with their relay disabled is not supported.");
+
return true;
}
@@ -3708,21 +3909,13 @@ CWalletKey::CWalletKey(int64_t nExpires)
nTimeExpires = nExpires;
}
-int CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
+void CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)
{
- AssertLockHeld(cs_main);
-
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// set the position of the transaction in the block
nIndex = posInBlock;
-
- // Is the tx in a block that's in the main chain
- if (!chainActive.Contains(pindex))
- return 0;
-
- return chainActive.Height() - pindex->nHeight + 1;
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const
@@ -3754,5 +3947,5 @@ int CMerkleTx::GetBlocksToMaturity() const
bool CMerkleTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState& state)
{
- return ::AcceptToMemoryPool(mempool, state, *this, true, NULL, false, nAbsurdFee);
+ return ::AcceptToMemoryPool(mempool, state, tx, true, NULL, NULL, false, nAbsurdFee);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 66c9a1ca47..ea4787c363 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -13,11 +13,13 @@
#include "utilstrencodings.h"
#include "validationinterface.h"
#include "script/ismine.h"
+#include "script/sign.h"
#include "wallet/crypter.h"
#include "wallet/walletdb.h"
#include "wallet/rpcwallet.h"
#include <algorithm>
+#include <atomic>
#include <map>
#include <set>
#include <stdexcept>
@@ -47,12 +49,18 @@ static const CAmount DEFAULT_TRANSACTION_FEE = 0;
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
-//! minimum change amount
+//! minimum recommended increment for BIP 125 replacement txs
+static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
+//! target minimum change amount
static const CAmount MIN_CHANGE = CENT;
+//! final minimum change amount after paying for fees
+static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -sendfreetransactions
static const bool DEFAULT_SEND_FREE_TRANSACTIONS = false;
+//! Default for -walletrejectlongchains
+static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
//! -walletrbf default
@@ -216,7 +224,7 @@ public:
READWRITE(nIndex);
}
- int SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock);
+ void SetMerkleBranch(const CBlockIndex* pIndex, int posInBlock);
/**
* Return depth of transaction in blockchain:
@@ -372,6 +380,7 @@ public:
{
fCreditCached = false;
fAvailableCreditCached = false;
+ fImmatureCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
@@ -560,6 +569,8 @@ private:
class CWallet : public CCryptoKeyStore, public CValidationInterface
{
private:
+ static std::atomic<bool> fFlushThreadRunning;
+
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coinControl are selected; Never select unconfirmed coins
@@ -695,7 +706,7 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -777,7 +788,7 @@ public:
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
- bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const CTxDestination& destChange = CNoDestination());
+ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey = true, const CTxDestination& destChange = CNoDestination());
/**
* Create a new transaction paying the recipients with a set of coins
@@ -791,6 +802,8 @@ public:
void ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries);
bool AddAccountingEntry(const CAccountingEntry&);
bool AddAccountingEntry(const CAccountingEntry&, CWalletDB *pwalletdb);
+ template <typename ContainerType>
+ bool DummySignTx(CMutableTransaction &txNew, const ContainerType &coins);
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
@@ -800,6 +813,11 @@ public:
*/
static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool);
/**
+ * Estimate the minimum fee considering required fee and targetFee or if 0
+ * then fee estimation for nConfirmTarget
+ */
+ static CAmount GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee);
+ /**
* Return the minimum required fee taking into account the
* floating relay fee and user set minimum transaction fee
*/
@@ -822,6 +840,10 @@ public:
std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
isminetype IsMine(const CTxIn& txin) const;
+ /**
+ * Returns amount of debit if the input matches the
+ * filter, otherwise returns 0
+ */
CAmount GetDebit(const CTxIn& txin, const isminefilter& filter) const;
isminetype IsMine(const CTxOut& txout) const;
CAmount GetCredit(const CTxOut& txout, const isminefilter& filter) const;
@@ -831,6 +853,8 @@ public:
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction& tx) const;
CAmount GetDebit(const CTransaction& tx, const isminefilter& filter) const;
+ /** Returns whether all of the inputs match the filter */
+ bool IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetCredit(const CTransaction& tx, const isminefilter& filter) const;
CAmount GetChange(const CTransaction& tx) const;
void SetBestChain(const CBlockLocator& loc);
@@ -882,6 +906,9 @@ public:
//! Get wallet transactions that conflict with given transaction (spend same outputs)
std::set<uint256> GetConflicts(const uint256& txid) const;
+ //! Check if a given transaction has any of its outputs spent by another transaction in the wallet
+ bool HasWalletSpend(const uint256& txid) const;
+
//! Flush wallet (bitdb flush)
void Flush(bool shutdown=false);
@@ -918,10 +945,14 @@ public:
/* Mark a transaction (and it in-wallet descendants) as abandoned so its inputs may be respent. */
bool AbandonTransaction(const uint256& hashTx);
+ /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */
+ bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
+
/* Returns the wallets help message */
static std::string GetWalletHelpString(bool showDebug);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
+ static CWallet* CreateWalletFromFile(const std::string walletFile);
static bool InitLoadWallet();
/**
@@ -1005,4 +1036,28 @@ public:
}
};
+// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes)
+// ContainerType is meant to hold pair<CWalletTx *, int>, and be iterable
+// so that each entry corresponds to each vIn, in order.
+template <typename ContainerType>
+bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins)
+{
+ // Fill in dummy signatures for fee calculation.
+ int nIn = 0;
+ for (const auto& coin : coins)
+ {
+ const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;
+ SignatureData sigdata;
+
+ if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
+ {
+ return false;
+ } else {
+ UpdateTransaction(txNew, nIn, sigdata);
+ }
+
+ nIn++;
+ }
+ return true;
+}
#endif // BITCOIN_WALLET_WALLET_H
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 0395e4a072..9cd19ab619 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -768,7 +768,7 @@ DBErrors CWalletDB::ZapWalletTx(CWallet* pwallet, vector<CWalletTx>& vWtx)
return DB_LOAD_OK;
}
-void ThreadFlushWalletDB(const string& strFile)
+void ThreadFlushWalletDB()
{
// Make this thread recognisable as the wallet flushing thread
RenameThread("bitcoin-wallet");
@@ -810,6 +810,7 @@ void ThreadFlushWalletDB(const string& strFile)
if (nRefCount == 0)
{
boost::this_thread::interruption_point();
+ const std::string& strFile = pwalletMain->strWalletFile;
map<string, int>::iterator _mi = bitdb.mapFileUseCount.find(strFile);
if (_mi != bitdb.mapFileUseCount.end())
{
diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h
index eb25ac613d..b9db55baa4 100644
--- a/src/wallet/walletdb.h
+++ b/src/wallet/walletdb.h
@@ -1,5 +1,5 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
-// Copyright (c) 2009-2015 The Bitcoin Core developers
+// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
@@ -182,6 +182,6 @@ private:
};
-void ThreadFlushWalletDB(const std::string& strFile);
+void ThreadFlushWalletDB();
#endif // BITCOIN_WALLET_WALLETDB_H