Page MenuHomePhabricator

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 316aca2fd..510f70854 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -1,3684 +1,3684 @@
// Copyright (c) 2010 Satoshi Nakamoto
// 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.
#include "amount.h"
#include "chain.h"
#include "chainparams.h" // for GetConsensus.
#include "config.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "dstencode.h"
#include "net.h"
#include "policy/fees.h"
#include "policy/policy.h"
#include "rpc/mining.h"
#include "rpc/misc.h"
#include "rpc/safemode.h"
#include "rpc/server.h"
#include "timedata.h"
#include "util.h"
#include "utilmoneystr.h"
#include "validation.h"
#include "wallet.h"
#include "wallet/coincontrol.h"
#include "wallet/wallet.h"
#include "wallet/walletdb.h"
#include "walletdb.h"
// Input src/init.h (not wallet/init.h) for StartShutdown
#include <init.h>
#include <univalue.h>
#include <event2/http.h>
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static std::string urlDecode(const std::string &urlEncoded) {
std::string res;
if (!urlEncoded.empty()) {
char *decoded = evhttp_uridecode(urlEncoded.c_str(), false, nullptr);
if (decoded) {
res = std::string(decoded);
free(decoded);
}
}
return res;
}
CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest &request) {
if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) ==
WALLET_ENDPOINT_BASE) {
// wallet endpoint was used
std::string requestedWallet =
urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size()));
for (CWalletRef pwallet : ::vpwallets) {
if (pwallet->GetName() == requestedWallet) {
return pwallet;
}
}
throw JSONRPCError(RPC_WALLET_NOT_FOUND,
"Requested wallet does not exist or is not loaded");
}
return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0)
? ::vpwallets[0]
: nullptr;
}
std::string HelpRequiringPassphrase(CWallet *const pwallet) {
return pwallet && pwallet->IsCrypted() ? "\nRequires wallet passphrase to "
"be set with walletpassphrase "
"call."
: "";
}
bool EnsureWalletIsAvailable(CWallet *const pwallet, bool avoidException) {
if (pwallet) {
return true;
}
if (avoidException) {
return false;
}
if (::vpwallets.empty()) {
// Note: It isn't currently possible to trigger this error because
// wallet RPC methods aren't registered unless a wallet is loaded. But
// this error is being kept as a precaution, because it's possible in
// the future that wallet RPC methods might get or remain registered
// when no wallets are loaded.
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (wallet "
"method is disabled because "
"no wallet is loaded)");
}
throw JSONRPCError(RPC_WALLET_NOT_SPECIFIED,
"Wallet file not specified (must request wallet RPC "
"through /wallet/<filename> uri-path).");
}
void EnsureWalletIsUnlocked(CWallet *const pwallet) {
if (pwallet->IsLocked()) {
throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the "
"wallet passphrase with "
"walletpassphrase first.");
}
}
void WalletTxToJSON(const CWalletTx &wtx, UniValue &entry) {
int confirms = wtx.GetDepthInMainChain();
entry.push_back(Pair("confirmations", confirms));
if (wtx.IsCoinBase()) {
entry.push_back(Pair("generated", true));
}
if (confirms > 0) {
entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex()));
entry.push_back(Pair("blockindex", wtx.nIndex));
entry.push_back(
Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime()));
} else {
entry.push_back(Pair("trusted", wtx.IsTrusted()));
}
uint256 hash = wtx.GetId();
entry.push_back(Pair("txid", hash.GetHex()));
UniValue conflicts(UniValue::VARR);
for (const uint256 &conflict : wtx.GetConflicts()) {
conflicts.push_back(conflict.GetHex());
}
entry.push_back(Pair("walletconflicts", conflicts));
entry.push_back(Pair("time", wtx.GetTxTime()));
entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived));
for (const std::pair<std::string, std::string> &item : wtx.mapValue) {
entry.push_back(Pair(item.first, item.second));
}
}
std::string AccountFromValue(const UniValue &value) {
std::string strAccount = value.get_str();
if (strAccount == "*") {
throw JSONRPCError(RPC_WALLET_INVALID_ACCOUNT_NAME,
"Invalid account name");
}
return strAccount;
}
static UniValue getnewaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getnewaddress ( \"account\" )\n"
"\nReturns a new Bitcoin address for receiving payments.\n"
"If 'account' is specified (DEPRECATED), it is added to the "
"address book \n"
"so payments received with the address will be credited to "
"'account'.\n"
"\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"
"\"address\" (string) The new bitcoin address\n"
"\nExamples:\n" +
HelpExampleCli("getnewaddress", "") +
HelpExampleRpc("getnewaddress", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the account first so we don't generate a key if there's an error
std::string strAccount;
if (request.params.size() > 0) {
strAccount = AccountFromValue(request.params[0]);
}
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
// Generate a new key that is added to wallet
CPubKey newKey;
if (!pwallet->GetKeyFromPool(newKey)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
CKeyID keyID = newKey.GetID();
pwallet->SetAddressBook(keyID, strAccount, "receive");
return EncodeDestination(keyID);
}
CTxDestination GetAccountAddress(CWallet *const pwallet, std::string strAccount,
bool bForceNew = false) {
CPubKey pubKey;
if (!pwallet->GetAccountPubkey(pubKey, strAccount, bForceNew)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
return pubKey.GetID();
}
static UniValue getaccountaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaccountaddress \"account\"\n"
"\nDEPRECATED. Returns the current Bitcoin address for receiving "
"payments to this account.\n"
"\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"
"\"address\" (string) The account bitcoin address\n"
"\nExamples:\n" +
HelpExampleCli("getaccountaddress", "") +
HelpExampleCli("getaccountaddress", "\"\"") +
HelpExampleCli("getaccountaddress", "\"myaccount\"") +
HelpExampleRpc("getaccountaddress", "\"myaccount\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Parse the account first so we don't generate a key if there's an error
std::string strAccount = AccountFromValue(request.params[0]);
UniValue ret(UniValue::VSTR);
ret = EncodeDestination(GetAccountAddress(pwallet, strAccount));
return ret;
}
static UniValue getrawchangeaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getrawchangeaddress\n"
"\nReturns a new Bitcoin address, for receiving change.\n"
"This is for use with raw transactions, NOT normal use.\n"
"\nResult:\n"
"\"address\" (string) The address\n"
"\nExamples:\n" +
HelpExampleCli("getrawchangeaddress", "") +
HelpExampleRpc("getrawchangeaddress", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->IsLocked()) {
pwallet->TopUpKeyPool();
}
CReserveKey reservekey(pwallet);
CPubKey vchPubKey;
if (!reservekey.GetReservedKey(vchPubKey, true)) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
reservekey.KeepKey();
CKeyID keyID = vchPubKey.GetID();
return EncodeDestination(keyID);
}
static UniValue setaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"setaccount \"address\" \"account\"\n"
"\nDEPRECATED. Sets the account associated with the given "
"address.\n"
"\nArguments:\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",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"tabby\"") +
HelpExampleRpc(
"setaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"tabby\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
std::string strAccount;
if (request.params.size() > 1) {
strAccount = AccountFromValue(request.params[1]);
}
// Only add the account if the address is yours.
if (IsMine(*pwallet, dest)) {
// Detect when changing the account of an address that is the 'unused
// current key' of another account:
if (pwallet->mapAddressBook.count(dest)) {
std::string strOldAccount = pwallet->mapAddressBook[dest].name;
if (dest == GetAccountAddress(pwallet, strOldAccount)) {
GetAccountAddress(pwallet, strOldAccount, true);
}
}
pwallet->SetAddressBook(dest, strAccount, "receive");
} else {
throw JSONRPCError(RPC_MISC_ERROR,
"setaccount can only be used with own address");
}
return NullUniValue;
}
static UniValue getaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaccount \"address\"\n"
"\nDEPRECATED. Returns the account associated with the given "
"address.\n"
"\nArguments:\n"
"1. \"address\" (string, required) The bitcoin address for "
"account lookup.\n"
"\nResult:\n"
"\"accountname\" (string) the account address\n"
"\nExamples:\n" +
HelpExampleCli("getaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
HelpExampleRpc("getaccount",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
std::string strAccount;
std::map<CTxDestination, CAddressBookData>::iterator mi =
pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end() && !(*mi).second.name.empty()) {
strAccount = (*mi).second.name;
}
return strAccount;
}
static UniValue getaddressesbyaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"getaddressesbyaccount \"account\"\n"
"\nDEPRECATED. Returns the list of addresses for the given "
"account.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The account name.\n"
"\nResult:\n"
"[ (json array of string)\n"
" \"address\" (string) a bitcoin address associated with "
"the given account\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("getaddressesbyaccount", "\"tabby\"") +
HelpExampleRpc("getaddressesbyaccount", "\"tabby\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = AccountFromValue(request.params[0]);
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
for (const std::pair<CTxDestination, CAddressBookData> &item :
pwallet->mapAddressBook) {
const CTxDestination &dest = item.first;
const std::string &strName = item.second.name;
if (strName == strAccount) {
ret.push_back(EncodeDestination(dest));
}
}
return ret;
}
static void SendMoney(CWallet *const pwallet, const CTxDestination &address,
Amount nValue, bool fSubtractFeeFromAmount,
CWalletTx &wtxNew) {
Amount curBalance = pwallet->GetBalance();
// Check amount
if (nValue <= Amount::zero()) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount");
}
if (nValue > curBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
}
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
// Parse Bitcoin address
CScript scriptPubKey = GetScriptForDestination(address);
// Create and send the transaction
CReserveKey reservekey(pwallet);
Amount nFeeRequired;
std::string strError;
std::vector<CRecipient> vecSend;
int nChangePosRet = -1;
CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired,
nChangePosRet, strError)) {
if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) {
strError = strprintf("Error: This transaction requires a "
"transaction fee of at least %s",
FormatMoney(nFeeRequired));
}
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
if (!pwallet->CommitTransaction(wtxNew, reservekey, g_connman.get(),
state)) {
strError =
strprintf("Error: The transaction was rejected! Reason given: %s",
state.GetRejectReason());
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
}
static UniValue sendtoaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 5) {
throw std::runtime_error(
"sendtoaddress \"address\" amount ( \"comment\" \"comment_to\" "
"subtractfeefromamount )\n"
"\nSend an amount to a given address.\n" +
HelpRequiringPassphrase(pwallet) +
"\nArguments:\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"
" 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"
"\"txid\" (string) The transaction id.\n"
"\nExamples:\n" +
HelpExampleCli("sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") +
HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
"dd\" 0.1 \"donation\" \"seans "
"outpost\"") +
HelpExampleCli(
"sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") +
HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvay"
"dd\", 0.1, \"donation\", \"seans "
"outpost\""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
}
// Amount
Amount nAmount = AmountFromValue(request.params[1]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
// Wallet comments
CWalletTx wtx;
if (request.params.size() > 2 && !request.params[2].isNull() &&
!request.params[2].get_str().empty()) {
wtx.mapValue["comment"] = request.params[2].get_str();
}
if (request.params.size() > 3 && !request.params[3].isNull() &&
!request.params[3].get_str().empty()) {
wtx.mapValue["to"] = request.params[3].get_str();
}
bool fSubtractFeeFromAmount = false;
if (request.params.size() > 4) {
fSubtractFeeFromAmount = request.params[4].get_bool();
}
EnsureWalletIsUnlocked(pwallet);
SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, wtx);
return wtx.GetId().GetHex();
}
static UniValue listaddressgroupings(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp) {
throw std::runtime_error(
"listaddressgroupings\n"
"\nLists groups of addresses which have had their common "
"ownership\n"
"made public by common use as inputs or as the resulting change\n"
"in past transactions\n"
"\nResult:\n"
"[\n"
" [\n"
" [\n"
" \"address\", (string) The bitcoin address\n"
" amount, (numeric) The amount in " +
CURRENCY_UNIT + "\n"
" \"account\" (string, optional) "
"DEPRECATED. The account\n"
" ]\n"
" ,...\n"
" ]\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listaddressgroupings", "") +
HelpExampleRpc("listaddressgroupings", ""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
UniValue jsonGroupings(UniValue::VARR);
std::map<CTxDestination, Amount> balances = pwallet->GetAddressBalances();
for (const std::set<CTxDestination> &grouping :
pwallet->GetAddressGroupings()) {
UniValue jsonGrouping(UniValue::VARR);
for (const CTxDestination &address : grouping) {
UniValue addressInfo(UniValue::VARR);
addressInfo.push_back(EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address]));
if (pwallet->mapAddressBook.find(address) !=
pwallet->mapAddressBook.end()) {
addressInfo.push_back(
pwallet->mapAddressBook.find(address)->second.name);
}
jsonGrouping.push_back(addressInfo);
}
jsonGroupings.push_back(jsonGrouping);
}
return jsonGroupings;
}
static UniValue signmessage(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 2) {
throw std::runtime_error(
"signmessage \"address\" \"message\"\n"
"\nSign a message with the private key of an address" +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\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"
"\nExamples:\n"
"\nUnlock the wallet for 30 seconds\n" +
HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") +
"\nCreate the signature\n" +
HelpExampleCli(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" \"my message\"") +
"\nVerify the signature\n" +
HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4"
"XX\" \"signature\" \"my "
"message\"") +
"\nAs json rpc\n" +
HelpExampleRpc(
"signmessage",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", \"my message\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
EnsureWalletIsUnlocked(pwallet);
std::string strAddress = request.params[0].get_str();
std::string strMessage = request.params[1].get_str();
CTxDestination dest =
DecodeDestination(strAddress, config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address");
}
const CKeyID *keyID = boost::get<CKeyID>(&dest);
if (!keyID) {
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
CKey key;
if (!pwallet->GetKey(*keyID, key)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
}
CHashWriter ss(SER_GETHASH, 0);
ss << strMessageMagic;
ss << strMessage;
std::vector<uint8_t> vchSig;
if (!key.SignCompact(ss.GetHash(), vchSig)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed");
}
return EncodeBase64(&vchSig[0], vchSig.size());
}
static UniValue getreceivedbyaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getreceivedbyaddress \"address\" ( minconf )\n"
"\nReturns the total amount received by the given address in "
"transactions with at least minconf confirmations.\n"
"\nArguments:\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"
"\nExamples:\n"
"\nThe amount from transactions with at least 1 confirmation\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\"") +
"\nThe amount including unconfirmed transactions, zero "
"confirmations\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 0") +
"\nThe amount with at least 6 confirmation, very safe\n" +
HelpExampleCli("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\" 6") +
"\nAs a json rpc call\n" +
HelpExampleRpc("getreceivedbyaddress",
"\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\", 6"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
// Bitcoin address
CTxDestination dest =
DecodeDestination(request.params[0].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
CScript scriptPubKey = GetScriptForDestination(dest);
if (!IsMine(*pwallet, scriptPubKey)) {
return ValueFromAmount(Amount::zero());
}
// Minimum confirmations
int nMinDepth = 1;
if (request.params.size() > 1) {
nMinDepth = request.params[1].get_int();
}
// Tally
Amount nAmount = Amount::zero();
for (const std::pair<uint256, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() ||
!ContextualCheckTransactionForCurrentBlock(config, *wtx.tx,
state)) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
if (txout.scriptPubKey == scriptPubKey) {
if (wtx.GetDepthInMainChain() >= nMinDepth) {
nAmount += txout.nValue;
}
}
}
}
return ValueFromAmount(nAmount);
}
static UniValue getreceivedbyaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"getreceivedbyaccount \"account\" ( minconf )\n"
"\nDEPRECATED. Returns the total amount received by addresses with "
"<account> in transactions with at least [minconf] confirmations.\n"
"\nArguments:\n"
"1. \"account\" (string, required) The selected account, may "
"be the default account using \"\".\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 for this account.\n"
"\nExamples:\n"
"\nAmount received by the default account with at "
"least 1 confirmation\n" +
HelpExampleCli("getreceivedbyaccount", "\"\"") +
"\nAmount received at the tabby account including unconfirmed "
"amounts with zero confirmations\n" +
HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") +
"\nThe amount with at least 6 confirmation, very safe\n" +
HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") +
"\nAs a json rpc call\n" +
HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
// Minimum confirmations
int nMinDepth = 1;
if (request.params.size() > 1) {
nMinDepth = request.params[1].get_int();
}
// Get the set of pub keys assigned to account
std::string strAccount = AccountFromValue(request.params[0]);
std::set<CTxDestination> setAddress =
pwallet->GetAccountAddresses(strAccount);
// Tally
Amount nAmount = Amount::zero();
for (const std::pair<uint256, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() ||
!ContextualCheckTransactionForCurrentBlock(config, *wtx.tx,
state)) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
CTxDestination address;
if (ExtractDestination(txout.scriptPubKey, address) &&
IsMine(*pwallet, address) && setAddress.count(address)) {
if (wtx.GetDepthInMainChain() >= nMinDepth) {
nAmount += txout.nValue;
}
}
}
}
return ValueFromAmount(nAmount);
}
static UniValue getbalance(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 3) {
throw std::runtime_error(
"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 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 "
"temporarily resulting in low\n"
" or even negative balances.\n"
" In general, account balance calculation is "
"not considered reliable and\n"
" has resulted in confusing outcomes, so it is "
"recommended to avoid passing\n"
" 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"
"\nThe total amount in the wallet\n" +
HelpExampleCli("getbalance", "") +
"\nThe total amount in the wallet at least 5 blocks confirmed\n" +
HelpExampleCli("getbalance", "\"*\" 6") + "\nAs a json rpc call\n" +
HelpExampleRpc("getbalance", "\"*\", 6"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
if (request.params.size() == 0) {
return ValueFromAmount(pwallet->GetBalance());
}
const std::string *account = request.params[0].get_str() != "*"
? &request.params[0].get_str()
: nullptr;
int nMinDepth = 1;
if (request.params.size() > 1) {
nMinDepth = request.params[1].get_int();
}
isminefilter filter = ISMINE_SPENDABLE;
if (request.params.size() > 2 && request.params[2].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
return ValueFromAmount(
pwallet->GetLegacyBalance(filter, nMinDepth, account));
}
static UniValue getunconfirmedbalance(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"getunconfirmedbalance\n"
"Returns the server's total unconfirmed balance\n");
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
return ValueFromAmount(pwallet->GetUnconfirmedBalance());
}
static UniValue movecmd(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 3 ||
request.params.size() > 5) {
throw std::runtime_error(
"move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" "
")\n"
"\nDEPRECATED. Move a specified amount from one account in your "
"wallet to another.\n"
"\nArguments:\n"
"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. (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"
"\nExamples:\n"
"\nMove 0.01 " +
CURRENCY_UNIT +
" from the default account to the account named tabby\n" +
HelpExampleCli("move", "\"\" \"tabby\" 0.01") + "\nMove 0.01 " +
CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 "
"confirmations\n" +
HelpExampleCli("move",
"\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc(
"move",
"\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strFrom = AccountFromValue(request.params[0]);
std::string strTo = AccountFromValue(request.params[1]);
Amount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
if (request.params.size() > 3) {
// Unused parameter, used to be nMinDepth, keep type-checking it though.
(void)request.params[3].get_int();
}
std::string strComment;
if (request.params.size() > 4) {
strComment = request.params[4].get_str();
}
if (!pwallet->AccountMove(strFrom, strTo, nAmount, strComment)) {
throw JSONRPCError(RPC_DATABASE_ERROR, "database error");
}
return true;
}
static UniValue sendfrom(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 3 ||
request.params.size() > 6) {
throw std::runtime_error(
"sendfrom \"fromaccount\" \"toaddress\" amount ( minconf "
"\"comment\" \"comment_to\" )\n"
"\nDEPRECATED (use sendtoaddress). Sent an amount from an account "
"to a bitcoin address." +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) The name of the "
"account to send funds from. May be the default account using "
"\"\".\n"
" Specifying an account does not influence "
"coin selection, but it does associate the newly created\n"
" transaction with the account, so the "
"account's balance computation and transaction history can "
"reflect\n"
" the spend.\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"
" 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"
"\"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") +
"\nSend 0.01 from the tabby account to the given address, funds "
"must have at least 6 confirmations\n" +
HelpExampleCli("sendfrom",
"\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" "
"0.01 6 \"donation\" \"seans outpost\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendfrom",
"\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", "
"0.01, 6, \"donation\", \"seans outpost\""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = AccountFromValue(request.params[0]);
CTxDestination dest =
DecodeDestination(request.params[1].get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid Bitcoin address");
}
Amount nAmount = AmountFromValue(request.params[2]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
int nMinDepth = 1;
if (request.params.size() > 3) {
nMinDepth = request.params[3].get_int();
}
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (request.params.size() > 4 && !request.params[4].isNull() &&
!request.params[4].get_str().empty()) {
wtx.mapValue["comment"] = request.params[4].get_str();
}
if (request.params.size() > 5 && !request.params[5].isNull() &&
!request.params[5].get_str().empty()) {
wtx.mapValue["to"] = request.params[5].get_str();
}
EnsureWalletIsUnlocked(pwallet);
// Check funds
Amount nBalance =
pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (nAmount > nBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
"Account has insufficient funds");
}
SendMoney(pwallet, dest, nAmount, false, wtx);
return wtx.GetId().GetHex();
}
static UniValue sendmany(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 5) {
throw std::runtime_error(
"sendmany \"fromaccount\" {\"address\":amount,...} ( minconf "
"\"comment\" [\"address\",...] )\n"
"\nSend multiple times. Amounts are double-precision floating "
"point numbers." +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments:\n"
"1. \"fromaccount\" (string, required) DEPRECATED. The "
"account to send the funds from. Should be \"\" for the default "
"account\n"
"2. \"amounts\" (string, required) A json object with "
"addresses and amounts\n"
" {\n"
" \"address\":amount (numeric or string) The bitcoin "
"address is the key, the numeric amount (can be string) in " +
CURRENCY_UNIT +
" is the value\n"
" ,...\n"
" }\n"
"3. minconf (numeric, optional, default=1) Only "
"use the balance confirmed at least this many times.\n"
"4. \"comment\" (string, optional) A comment\n"
"5. subtractfeefrom (array, optional) A json array with "
"addresses.\n"
" The fee will be equally deducted from "
"the amount of each selected address.\n"
" Those recipients will receive less "
"bitcoins than you enter in their corresponding amount field.\n"
" If no addresses are specified here, "
"the sender pays the fee.\n"
" [\n"
" \"address\" (string) Subtract fee from this "
"address\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"\"txid\" (string) The transaction id for the "
"send. Only 1 transaction is created regardless of \n"
" the number of addresses.\n"
"\nExamples:\n"
"\nSend two amounts to two different addresses:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}"
"\"") +
"\nSend two amounts to two different addresses setting the "
"confirmation and comment:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
"6 \"testing\"") +
"\nSend two amounts to two different addresses, subtract fee from "
"amount:\n" +
HelpExampleCli("sendmany",
"\"\" "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" "
"1 \"\" "
"\"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\","
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("sendmany",
"\"\", "
"\"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XX\\\":0.01,"
"\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\","
" 6, \"testing\""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
std::string strAccount = AccountFromValue(request.params[0]);
UniValue sendTo = request.params[1].get_obj();
int nMinDepth = 1;
if (request.params.size() > 2) {
nMinDepth = request.params[2].get_int();
}
CWalletTx wtx;
wtx.strFromAccount = strAccount;
if (request.params.size() > 3 && !request.params[3].isNull() &&
!request.params[3].get_str().empty()) {
wtx.mapValue["comment"] = request.params[3].get_str();
}
UniValue subtractFeeFromAmount(UniValue::VARR);
if (request.params.size() > 4) {
subtractFeeFromAmount = request.params[4].get_array();
}
std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
Amount totalAmount = Amount::zero();
std::vector<std::string> keys = sendTo.getKeys();
for (const std::string &name_ : keys) {
CTxDestination dest = DecodeDestination(name_, config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
std::string("Invalid Bitcoin address: ") +
name_);
}
if (destinations.count(dest)) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated address: ") + name_);
}
destinations.insert(dest);
CScript scriptPubKey = GetScriptForDestination(dest);
Amount nAmount = AmountFromValue(sendTo[name_]);
if (nAmount <= Amount::zero()) {
throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send");
}
totalAmount += nAmount;
bool fSubtractFeeFromAmount = false;
for (size_t idx = 0; idx < subtractFeeFromAmount.size(); idx++) {
const UniValue &addr = subtractFeeFromAmount[idx];
if (addr.get_str() == name_) {
fSubtractFeeFromAmount = true;
}
}
CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount};
vecSend.push_back(recipient);
}
EnsureWalletIsUnlocked(pwallet);
// Check funds
Amount nBalance =
pwallet->GetLegacyBalance(ISMINE_SPENDABLE, nMinDepth, &strAccount);
if (totalAmount > nBalance) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS,
"Account has insufficient funds");
}
// Send
CReserveKey keyChange(pwallet);
Amount nFeeRequired = Amount::zero();
int nChangePosRet = -1;
std::string strFailReason;
bool fCreated = pwallet->CreateTransaction(
vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason);
if (!fCreated) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
}
CValidationState state;
if (!pwallet->CommitTransaction(wtx, keyChange, g_connman.get(), state)) {
strFailReason = strprintf("Transaction commit failed:: %s",
state.GetRejectReason());
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
return wtx.GetId().GetHex();
}
static UniValue addmultisigaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
std::string msg =
"addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n"
"\nAdd a nrequired-to-sign multisignature address to the wallet. "
"Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"If 'account' is specified (DEPRECATED), assign address to that "
"account.\n"
"\nArguments:\n"
"1. nrequired (numeric, required) The number of required "
"signatures out of the n keys or addresses.\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"
" ]\n"
"3. \"account\" (string, optional) DEPRECATED. An account to "
"assign the addresses to.\n"
"\nResult:\n"
"\"address\" (string) A bitcoin address associated with "
"the keys.\n"
"\nExamples:\n"
"\nAdd a multisig address from 2 addresses\n" +
HelpExampleCli("addmultisigaddress",
"2 "
"\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
"\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") +
"\nAs json rpc call\n" +
HelpExampleRpc("addmultisigaddress",
"2, "
"\"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\","
"\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"");
throw std::runtime_error(msg);
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount;
if (request.params.size() > 2) {
strAccount = AccountFromValue(request.params[2]);
}
// Construct using pay-to-script-hash:
CScript inner = createmultisig_redeemScript(pwallet, request.params);
CScriptID innerID(inner);
pwallet->AddCScript(inner);
pwallet->SetAddressBook(innerID, strAccount, "send");
return EncodeDestination(innerID);
}
struct tallyitem {
Amount nAmount;
int nConf;
std::vector<uint256> txids;
bool fIsWatchonly;
tallyitem() {
nAmount = Amount::zero();
nConf = std::numeric_limits<int>::max();
fIsWatchonly = false;
}
};
static UniValue ListReceived(const Config &config, CWallet *const pwallet,
const UniValue &params, bool fByAccounts) {
// Minimum confirmations
int nMinDepth = 1;
if (params.size() > 0) {
nMinDepth = params[0].get_int();
}
// Whether to include empty accounts
bool fIncludeEmpty = false;
if (params.size() > 1) {
fIncludeEmpty = params[1].get_bool();
}
isminefilter filter = ISMINE_SPENDABLE;
if (params.size() > 2 && params[2].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
// Tally
std::map<CTxDestination, tallyitem> mapTally;
for (const std::pair<uint256, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
CValidationState state;
if (wtx.IsCoinBase() ||
!ContextualCheckTransactionForCurrentBlock(config, *wtx.tx,
state)) {
continue;
}
int nDepth = wtx.GetDepthInMainChain();
if (nDepth < nMinDepth) {
continue;
}
for (const CTxOut &txout : wtx.tx->vout) {
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address)) {
continue;
}
isminefilter mine = IsMine(*pwallet, address);
if (!(mine & filter)) {
continue;
}
tallyitem &item = mapTally[address];
item.nAmount += txout.nValue;
item.nConf = std::min(item.nConf, nDepth);
item.txids.push_back(wtx.GetId());
if (mine & ISMINE_WATCH_ONLY) {
item.fIsWatchonly = true;
}
}
}
// Reply
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> mapAccountTally;
for (const std::pair<CTxDestination, CAddressBookData> &item :
pwallet->mapAddressBook) {
const CTxDestination &dest = item.first;
const std::string &strAccount = item.second.name;
std::map<CTxDestination, tallyitem>::iterator it = mapTally.find(dest);
if (it == mapTally.end() && !fIncludeEmpty) {
continue;
}
Amount nAmount = Amount::zero();
int nConf = std::numeric_limits<int>::max();
bool fIsWatchonly = false;
if (it != mapTally.end()) {
nAmount = (*it).second.nAmount;
nConf = (*it).second.nConf;
fIsWatchonly = (*it).second.fIsWatchonly;
}
if (fByAccounts) {
tallyitem &_item = mapAccountTally[strAccount];
_item.nAmount += nAmount;
_item.nConf = std::min(_item.nConf, nConf);
_item.fIsWatchonly = fIsWatchonly;
} else {
UniValue obj(UniValue::VOBJ);
if (fIsWatchonly) {
obj.push_back(Pair("involvesWatchonly", true));
}
obj.push_back(Pair("address", EncodeDestination(dest)));
obj.push_back(Pair("account", strAccount));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(
Pair("confirmations",
(nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
if (!fByAccounts) {
obj.push_back(Pair("label", strAccount));
}
UniValue transactions(UniValue::VARR);
if (it != mapTally.end()) {
for (const uint256 &_item : (*it).second.txids) {
transactions.push_back(_item.GetHex());
}
}
obj.push_back(Pair("txids", transactions));
ret.push_back(obj);
}
}
if (fByAccounts) {
for (std::map<std::string, tallyitem>::iterator it =
mapAccountTally.begin();
it != mapAccountTally.end(); ++it) {
Amount nAmount = (*it).second.nAmount;
int nConf = (*it).second.nConf;
UniValue obj(UniValue::VOBJ);
if ((*it).second.fIsWatchonly) {
obj.push_back(Pair("involvesWatchonly", true));
}
obj.push_back(Pair("account", (*it).first));
obj.push_back(Pair("amount", ValueFromAmount(nAmount)));
obj.push_back(
Pair("confirmations",
(nConf == std::numeric_limits<int>::max() ? 0 : nConf)));
ret.push_back(obj);
}
}
return ret;
}
static UniValue listreceivedbyaddress(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 3) {
throw std::runtime_error(
"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. 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"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if "
"imported addresses were involved in transaction\n"
" \"address\" : \"receivingaddress\", (string) The receiving "
"address\n"
" \"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"
" \"txids\": [\n"
" n, (numeric) The ids of "
"transactions received with the address \n"
" ...\n"
" ]\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listreceivedbyaddress", "") +
HelpExampleCli("listreceivedbyaddress", "6 true") +
HelpExampleRpc("listreceivedbyaddress", "6, true, true"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
return ListReceived(config, pwallet, request.params, false);
}
static UniValue listreceivedbyaccount(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 3) {
throw std::runtime_error(
"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. 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"
" {\n"
" \"involvesWatchonly\" : true, (bool) Only returned if "
"imported addresses were involved in transaction\n"
" \"account\" : \"accountname\", (string) The account name of "
"the receiving account\n"
" \"amount\" : x.xxx, (numeric) The total amount "
"received by addresses with this account\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"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listreceivedbyaccount", "") +
HelpExampleCli("listreceivedbyaccount", "6 true") +
HelpExampleRpc("listreceivedbyaccount", "6, true, true"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
return ListReceived(config, pwallet, request.params, true);
}
static void MaybePushAddress(UniValue &entry, const CTxDestination &dest) {
if (IsValidDestination(dest)) {
entry.push_back(Pair("address", EncodeDestination(dest)));
}
}
void ListTransactions(CWallet *const pwallet, const CWalletTx &wtx,
const std::string &strAccount, int nMinDepth, bool fLong,
UniValue &ret, const isminefilter &filter) {
Amount nFee;
std::string strSentAccount;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter);
bool fAllAccounts = (strAccount == std::string("*"));
bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY);
// Sent
if ((!listSent.empty() || nFee != Amount::zero()) &&
(fAllAccounts || strAccount == strSentAccount)) {
for (const COutputEntry &s : listSent) {
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly ||
(::IsMine(*pwallet, s.destination) & ISMINE_WATCH_ONLY)) {
entry.push_back(Pair("involvesWatchonly", true));
}
entry.push_back(Pair("account", strSentAccount));
MaybePushAddress(entry, s.destination);
entry.push_back(Pair("category", "send"));
entry.push_back(Pair("amount", ValueFromAmount(-s.amount)));
if (pwallet->mapAddressBook.count(s.destination)) {
entry.push_back(
Pair("label", pwallet->mapAddressBook[s.destination].name));
}
entry.push_back(Pair("vout", s.vout));
entry.push_back(Pair("fee", ValueFromAmount(-1 * nFee)));
if (fLong) {
WalletTxToJSON(wtx, entry);
}
entry.push_back(Pair("abandoned", wtx.isAbandoned()));
ret.push_back(entry);
}
}
// Received
if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) {
for (const COutputEntry &r : listReceived) {
std::string account;
if (pwallet->mapAddressBook.count(r.destination)) {
account = pwallet->mapAddressBook[r.destination].name;
}
if (fAllAccounts || (account == strAccount)) {
UniValue entry(UniValue::VOBJ);
if (involvesWatchonly ||
(::IsMine(*pwallet, r.destination) & ISMINE_WATCH_ONLY)) {
entry.push_back(Pair("involvesWatchonly", true));
}
entry.push_back(Pair("account", account));
MaybePushAddress(entry, r.destination);
if (wtx.IsCoinBase()) {
if (wtx.GetDepthInMainChain() < 1) {
entry.push_back(Pair("category", "orphan"));
- } else if (wtx.GetBlocksToMaturity() > 0) {
+ } else if (wtx.IsImmatureCoinBase()) {
entry.push_back(Pair("category", "immature"));
} else {
entry.push_back(Pair("category", "generate"));
}
} else {
entry.push_back(Pair("category", "receive"));
}
entry.push_back(Pair("amount", ValueFromAmount(r.amount)));
if (pwallet->mapAddressBook.count(r.destination)) {
entry.push_back(Pair("label", account));
}
entry.push_back(Pair("vout", r.vout));
if (fLong) {
WalletTxToJSON(wtx, entry);
}
ret.push_back(entry);
}
}
}
}
void AcentryToJSON(const CAccountingEntry &acentry,
const std::string &strAccount, UniValue &ret) {
bool fAllAccounts = (strAccount == std::string("*"));
if (fAllAccounts || acentry.strAccount == strAccount) {
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("account", acentry.strAccount));
entry.push_back(Pair("category", "move"));
entry.push_back(Pair("time", acentry.nTime));
entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit)));
entry.push_back(Pair("otheraccount", acentry.strOtherAccount));
entry.push_back(Pair("comment", acentry.strComment));
ret.push_back(entry);
}
}
static UniValue listtransactions(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 4) {
throw std::runtime_error(
"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. 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\":\"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"
" transaction id or "
"block. 'send' and 'receive' transactions are \n"
" associated with "
"an address, transaction id and block details\n"
" \"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"
" \"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"
" \"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"
" 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 (midnight Jan 1 1970 GMT).\n"
" \"timereceived\": xxx, (numeric) The time received in "
"seconds since epoch (midnight Jan 1 1970 GMT). Available \n"
" for 'send' and "
"'receive' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is "
"associated with the transaction.\n"
" \"otheraccount\": \"accountname\", (string) DEPRECATED. For "
"the 'move' category of transactions, the account the funds came \n"
" from (for receiving "
"funds, positive amounts), or went to (for sending funds,\n"
" negative amounts).\n"
" \"abandoned\": xxx (bool) 'true' if the transaction "
"has been abandoned (inputs are respendable). Only available for "
"the \n"
" 'send' category of "
"transactions.\n"
" }\n"
"]\n"
"\nExamples:\n"
"\nList the most recent 10 transactions in the systems\n" +
HelpExampleCli("listtransactions", "") +
"\nList transactions 100 to 120\n" +
HelpExampleCli("listtransactions", "\"*\" 20 100") +
"\nAs a json rpc call\n" +
HelpExampleRpc("listtransactions", "\"*\", 20, 100"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::string strAccount = "*";
if (request.params.size() > 0) {
strAccount = request.params[0].get_str();
}
int nCount = 10;
if (request.params.size() > 1) {
nCount = request.params[1].get_int();
}
int nFrom = 0;
if (request.params.size() > 2) {
nFrom = request.params[2].get_int();
}
isminefilter filter = ISMINE_SPENDABLE;
if (request.params.size() > 3 && request.params[3].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
if (nCount < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
}
if (nFrom < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from");
}
UniValue ret(UniValue::VARR);
const CWallet::TxItems &txOrdered = pwallet->wtxOrdered;
// iterate backwards until we have nCount items to return:
for (CWallet::TxItems::const_reverse_iterator it = txOrdered.rbegin();
it != txOrdered.rend(); ++it) {
CWalletTx *const pwtx = (*it).second.first;
if (pwtx != 0) {
ListTransactions(pwallet, *pwtx, strAccount, 0, true, ret, filter);
}
CAccountingEntry *const pacentry = (*it).second.second;
if (pacentry != 0) {
AcentryToJSON(*pacentry, strAccount, ret);
}
if ((int)ret.size() >= (nCount + nFrom)) {
break;
}
}
// ret is newest to oldest
if (nFrom > (int)ret.size()) {
nFrom = ret.size();
}
if ((nFrom + nCount) > (int)ret.size()) {
nCount = ret.size() - nFrom;
}
std::vector<UniValue> arrTmp = ret.getValues();
std::vector<UniValue>::iterator first = arrTmp.begin();
std::advance(first, nFrom);
std::vector<UniValue>::iterator last = arrTmp.begin();
std::advance(last, nFrom + nCount);
if (last != arrTmp.end()) {
arrTmp.erase(last, arrTmp.end());
}
if (first != arrTmp.begin()) {
arrTmp.erase(arrTmp.begin(), first);
}
// Return oldest to newest
std::reverse(arrTmp.begin(), arrTmp.end());
ret.clear();
ret.setArray();
ret.push_backV(arrTmp);
return ret;
}
static UniValue listaccounts(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"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. 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"
" ...\n"
"}\n"
"\nExamples:\n"
"\nList account balances where there at least 1 confirmation\n" +
HelpExampleCli("listaccounts", "") + "\nList account balances "
"including zero confirmation "
"transactions\n" +
HelpExampleCli("listaccounts", "0") +
"\nList account balances for 6 or more confirmations\n" +
HelpExampleCli("listaccounts", "6") + "\nAs json rpc call\n" +
HelpExampleRpc("listaccounts", "6"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
int nMinDepth = 1;
if (request.params.size() > 0) {
nMinDepth = request.params[0].get_int();
}
isminefilter includeWatchonly = ISMINE_SPENDABLE;
if (request.params.size() > 1 && request.params[1].get_bool()) {
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
}
std::map<std::string, Amount> mapAccountBalances;
for (const std::pair<CTxDestination, CAddressBookData> &entry :
pwallet->mapAddressBook) {
// This address belongs to me
if (IsMine(*pwallet, entry.first) & includeWatchonly) {
mapAccountBalances[entry.second.name] = Amount::zero();
}
}
for (const std::pair<uint256, CWalletTx> &pairWtx : pwallet->mapWallet) {
const CWalletTx &wtx = pairWtx.second;
Amount nFee;
std::string strSentAccount;
std::list<COutputEntry> listReceived;
std::list<COutputEntry> listSent;
int nDepth = wtx.GetDepthInMainChain();
- if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) {
+ if (wtx.IsImmatureCoinBase() || nDepth < 0) {
continue;
}
wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount,
includeWatchonly);
mapAccountBalances[strSentAccount] -= nFee;
for (const COutputEntry &s : listSent) {
mapAccountBalances[strSentAccount] -= s.amount;
}
if (nDepth >= nMinDepth) {
for (const COutputEntry &r : listReceived) {
if (pwallet->mapAddressBook.count(r.destination)) {
mapAccountBalances[pwallet->mapAddressBook[r.destination]
.name] += r.amount;
} else {
mapAccountBalances[""] += r.amount;
}
}
}
}
const std::list<CAccountingEntry> &acentries = pwallet->laccentries;
for (const CAccountingEntry &entry : acentries) {
mapAccountBalances[entry.strAccount] += entry.nCreditDebit;
}
UniValue ret(UniValue::VOBJ);
for (const std::pair<std::string, Amount> &accountBalance :
mapAccountBalances) {
ret.push_back(
Pair(accountBalance.first, ValueFromAmount(accountBalance.second)));
}
return ret;
}
static UniValue listsinceblock(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp) {
throw std::runtime_error(
"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. 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\":\"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"
" \"abandoned\": xxx, (bool) 'true' if the transaction "
"has been abandoned (inputs are respendable). Only available for "
"the 'send' category of transactions.\n"
" \"comment\": \"...\", (string) If a comment is "
"associated with the transaction.\n"
" \"label\" : \"label\" (string) A comment for the "
"address/transaction, if any\n"
" \"to\": \"...\", (string) If a comment to is "
"associated with the transaction.\n"
" ],\n"
" \"lastblock\": \"lastblockhash\" (string) The hash of the "
"last block\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("listsinceblock", "") +
HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc4"
"5ef753ee9a7d38571037cdb1a57f663ad"
"\" 6") +
HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc4"
"5ef753ee9a7d38571037cdb1a57f663ad"
"\", 6"));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
const CBlockIndex *pindex = nullptr;
int target_confirms = 1;
isminefilter filter = ISMINE_SPENDABLE;
if (request.params.size() > 0) {
uint256 blockId;
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) {
target_confirms = request.params[1].get_int();
if (target_confirms < 1) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
}
}
if (request.params.size() > 2 && request.params[2].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1;
UniValue transactions(UniValue::VARR);
for (const std::pair<uint256, CWalletTx> &pairWtx : pwallet->mapWallet) {
CWalletTx tx = pairWtx.second;
if (depth == -1 || tx.GetDepthInMainChain() < depth) {
ListTransactions(pwallet, tx, "*", 0, true, transactions, filter);
}
}
CBlockIndex *pblockLast =
chainActive[chainActive.Height() + 1 - target_confirms];
uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256();
UniValue ret(UniValue::VOBJ);
ret.push_back(Pair("transactions", transactions));
ret.push_back(Pair("lastblock", lastblock.GetHex()));
return ret;
}
static UniValue gettransaction(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"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. \"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"
" \"blocktime\" : ttt, (numeric) The time in seconds since "
"epoch (1 Jan 1970 GMT)\n"
" \"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"
" 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\" : \"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"
" \"hex\" : \"data\" (string) Raw data for transaction\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e211"
"5b9345e16c5cf302fc80e9d5fbf5d48d"
"\"") +
HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e211"
"5b9345e16c5cf302fc80e9d5fbf5d48d"
"\" true") +
HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e211"
"5b9345e16c5cf302fc80e9d5fbf5d48d"
"\""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
TxId txid;
txid.SetHex(request.params[0].get_str());
isminefilter filter = ISMINE_SPENDABLE;
if (request.params.size() > 1 && request.params[1].get_bool()) {
filter = filter | ISMINE_WATCH_ONLY;
}
UniValue entry(UniValue::VOBJ);
if (!pwallet->mapWallet.count(txid)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid or non-wallet transaction id");
}
const CWalletTx &wtx = pwallet->mapWallet[txid];
Amount nCredit = wtx.GetCredit(filter);
Amount nDebit = wtx.GetDebit(filter);
Amount nNet = nCredit - nDebit;
Amount nFee = (wtx.IsFromMe(filter) ? wtx.tx->GetValueOut() - nDebit
: Amount::zero());
entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee)));
if (wtx.IsFromMe(filter)) {
entry.push_back(Pair("fee", ValueFromAmount(nFee)));
}
WalletTxToJSON(wtx, entry);
UniValue details(UniValue::VARR);
ListTransactions(pwallet, wtx, "*", 0, false, details, filter);
entry.push_back(Pair("details", details));
std::string strHex =
EncodeHexTx(static_cast<CTransaction>(wtx), RPCSerializationFlags());
entry.push_back(Pair("hex", strHex));
return entry;
}
static UniValue abandontransaction(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"abandontransaction \"txid\"\n"
"\nMark in-wallet transaction <txid> as abandoned\n"
"This will mark this transaction and all its in-wallet descendants "
"as abandoned which will allow\n"
"for their inputs to be respent. It can be used to replace "
"\"stuck\" or evicted transactions.\n"
"It only works on transactions which are not included in a block "
"and are not currently in the mempool.\n"
"It has no effect on transactions which are already conflicted or "
"abandoned.\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("abandontransaction", "\"1075db55d416d3ca199f55b6084"
"e2115b9345e16c5cf302fc80e9d5f"
"bf5d48d\"") +
HelpExampleRpc("abandontransaction", "\"1075db55d416d3ca199f55b6084"
"e2115b9345e16c5cf302fc80e9d5f"
"bf5d48d\""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
TxId txid;
txid.SetHex(request.params[0].get_str());
if (!pwallet->mapWallet.count(txid)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Invalid or non-wallet transaction id");
}
if (!pwallet->AbandonTransaction(txid)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Transaction not eligible for abandonment");
}
return NullUniValue;
}
static UniValue backupwallet(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"backupwallet \"destination\"\n"
"\nSafely copies current wallet file to destination, which can be "
"a directory or a path with filename.\n"
"\nArguments:\n"
"1. \"destination\" (string) The destination directory or file\n"
"\nExamples:\n" +
HelpExampleCli("backupwallet", "\"backup.dat\"") +
HelpExampleRpc("backupwallet", "\"backup.dat\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
std::string strDest = request.params[0].get_str();
if (!pwallet->BackupWallet(strDest)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!");
}
return NullUniValue;
}
static UniValue keypoolrefill(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"keypoolrefill ( newsize )\n"
"\nFills the keypool." +
HelpRequiringPassphrase(pwallet) +
"\n"
"\nArguments\n"
"1. newsize (numeric, optional, default=100) "
"The new keypool size\n"
"\nExamples:\n" +
HelpExampleCli("keypoolrefill", "") +
HelpExampleRpc("keypoolrefill", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
// 0 is interpreted by TopUpKeyPool() as the default keypool size given by
// -keypool
unsigned int kpSize = 0;
if (request.params.size() > 0) {
if (request.params[0].get_int() < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected valid size.");
}
kpSize = (unsigned int)request.params[0].get_int();
}
EnsureWalletIsUnlocked(pwallet);
pwallet->TopUpKeyPool(kpSize);
if (pwallet->GetKeyPoolSize() < kpSize) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool.");
}
return NullUniValue;
}
static void LockWallet(CWallet *pWallet) {
LOCK(pWallet->cs_wallet);
pWallet->nRelockTime = 0;
pWallet->Lock();
}
static UniValue walletpassphrase(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
throw std::runtime_error(
"walletpassphrase \"passphrase\" timeout\n"
"\nStores the wallet decryption key in memory for 'timeout' "
"seconds.\n"
"This is needed prior to performing transactions related to "
"private keys such as sending bitcoins\n"
"\nArguments:\n"
"1. \"passphrase\" (string, required) The wallet passphrase\n"
"2. timeout (numeric, required) The time to keep the "
"decryption key in seconds.\n"
"\nNote:\n"
"Issuing the walletpassphrase command while the wallet is already "
"unlocked will set a new unlock\n"
"time that overrides the old one.\n"
"\nExamples:\n"
"\nunlock the wallet for 60 seconds\n" +
HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") +
"\nLock the wallet again (before 60 seconds)\n" +
HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" +
HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60"));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an unencrypted wallet, but "
"walletpassphrase was called.");
}
// Note that the walletpassphrase is stored in request.params[0] which is
// not mlock()ed
SecureString strWalletPass;
strWalletPass.reserve(100);
// TODO: get rid of this .c_str() by implementing
// SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin
// with.
strWalletPass = request.params[0].get_str().c_str();
if (strWalletPass.length() > 0) {
if (!pwallet->Unlock(strWalletPass)) {
throw JSONRPCError(
RPC_WALLET_PASSPHRASE_INCORRECT,
"Error: The wallet passphrase entered was incorrect.");
}
} else {
throw std::runtime_error(
"walletpassphrase <passphrase> <timeout>\n"
"Stores the wallet decryption key in memory for "
"<timeout> seconds.");
}
pwallet->TopUpKeyPool();
int64_t nSleepTime = request.params[1].get_int64();
pwallet->nRelockTime = GetTime() + nSleepTime;
RPCRunLater(strprintf("lockwallet(%s)", pwallet->GetName()),
boost::bind(LockWallet, pwallet), nSleepTime);
return NullUniValue;
}
static UniValue walletpassphrasechange(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 2)) {
throw std::runtime_error(
"walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n"
"\nChanges the wallet passphrase from 'oldpassphrase' to "
"'newpassphrase'.\n"
"\nArguments:\n"
"1. \"oldpassphrase\" (string) The current passphrase\n"
"2. \"newpassphrase\" (string) The new passphrase\n"
"\nExamples:\n" +
HelpExampleCli("walletpassphrasechange",
"\"old one\" \"new one\"") +
HelpExampleRpc("walletpassphrasechange",
"\"old one\", \"new one\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an unencrypted wallet, but "
"walletpassphrasechange was called.");
}
// TODO: get rid of these .c_str() calls by implementing
// SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin
// with.
SecureString strOldWalletPass;
strOldWalletPass.reserve(100);
strOldWalletPass = request.params[0].get_str().c_str();
SecureString strNewWalletPass;
strNewWalletPass.reserve(100);
strNewWalletPass = request.params[1].get_str().c_str();
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) {
throw std::runtime_error(
"walletpassphrasechange <oldpassphrase> <newpassphrase>\n"
"Changes the wallet passphrase from <oldpassphrase> to "
"<newpassphrase>.");
}
if (!pwallet->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) {
throw JSONRPCError(
RPC_WALLET_PASSPHRASE_INCORRECT,
"Error: The wallet passphrase entered was incorrect.");
}
return NullUniValue;
}
static UniValue walletlock(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (pwallet->IsCrypted() && (request.fHelp || request.params.size() != 0)) {
throw std::runtime_error(
"walletlock\n"
"\nRemoves the wallet encryption key from memory, locking the "
"wallet.\n"
"After calling this method, you will need to call walletpassphrase "
"again\n"
"before being able to call any methods which require the wallet to "
"be unlocked.\n"
"\nExamples:\n"
"\nSet the passphrase for 2 minutes to perform a transaction\n" +
HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") +
"\nPerform a send (requires passphrase set)\n" +
HelpExampleCli("sendtoaddress",
"\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") +
"\nClear the passphrase since we are done before 2 minutes is "
"up\n" +
HelpExampleCli("walletlock", "") + "\nAs json rpc call\n" +
HelpExampleRpc("walletlock", ""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (!pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an unencrypted wallet, but "
"walletlock was called.");
}
pwallet->Lock();
pwallet->nRelockTime = 0;
return NullUniValue;
}
static UniValue encryptwallet(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (!pwallet->IsCrypted() &&
(request.fHelp || request.params.size() != 1)) {
throw std::runtime_error(
"encryptwallet \"passphrase\"\n"
"\nEncrypts the wallet with 'passphrase'. This is for first time "
"encryption.\n"
"After this, any calls that interact with private keys such as "
"sending or signing \n"
"will require the passphrase to be set prior the making these "
"calls.\n"
"Use the walletpassphrase call for this, and then walletlock "
"call.\n"
"If the wallet is already encrypted, use the "
"walletpassphrasechange call.\n"
"Note that this will shutdown the server.\n"
"\nArguments:\n"
"1. \"passphrase\" (string) The pass phrase to encrypt the "
"wallet with. It must be at least 1 character, but should be "
"long.\n"
"\nExamples:\n"
"\nEncrypt you wallet\n" +
HelpExampleCli("encryptwallet", "\"my pass phrase\"") +
"\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", "\"address\" \"test message\"") +
"\nNow lock the wallet again by removing the passphrase\n" +
HelpExampleCli("walletlock", "") + "\nAs a json rpc call\n" +
HelpExampleRpc("encryptwallet", "\"my pass phrase\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.fHelp) {
return true;
}
if (pwallet->IsCrypted()) {
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE,
"Error: running with an encrypted wallet, but "
"encryptwallet was called.");
}
// TODO: get rid of this .c_str() by implementing
// SecureString::operator=(std::string)
// Alternately, find a way to make request.params[0] mlock()'d to begin
// with.
SecureString strWalletPass;
strWalletPass.reserve(100);
strWalletPass = request.params[0].get_str().c_str();
if (strWalletPass.length() < 1) {
throw std::runtime_error("encryptwallet <passphrase>\n"
"Encrypts the wallet with <passphrase>.");
}
if (!pwallet->EncryptWallet(strWalletPass)) {
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED,
"Error: Failed to encrypt the wallet.");
}
// BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is
// unencrypted private keys. So:
StartShutdown();
return "wallet encrypted; Bitcoin server stopping, restart to run with "
"encrypted wallet. The keypool has been flushed and a new HD seed "
"was generated (if you are using HD). You need to make a new "
"backup.";
}
static UniValue lockunspent(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"lockunspent unlock ([{\"txid\":\"txid\",\"vout\":n},...])\n"
"\nUpdates list of temporarily unspendable outputs.\n"
"Temporarily lock (unlock=false) or unlock (unlock=true) specified "
"transaction outputs.\n"
"If no transaction outputs are specified when unlocking then all "
"current locked transaction outputs are unlocked.\n"
"A locked transaction output will not be chosen by automatic coin "
"selection, when spending bitcoins.\n"
"Locks are stored in memory only. Nodes start with zero locked "
"outputs, and the locked output list\n"
"is always cleared (by virtue of process exit) when a node stops "
"or fails.\n"
"Also see the listunspent call\n"
"\nArguments:\n"
"1. unlock (boolean, required) Whether to unlock (true) "
"or lock (false) the specified transactions\n"
"2. \"transactions\" (string, optional) A json array of objects. "
"Each object the txid (string) vout (numeric)\n"
" [ (json array of json objects)\n"
" {\n"
" \"txid\":\"id\", (string) The transaction id\n"
" \"vout\": n (numeric) The output number\n"
" }\n"
" ,...\n"
" ]\n"
"\nResult:\n"
"true|false (boolean) Whether the command was successful or "
"not\n"
"\nExamples:\n"
"\nList the unspent transactions\n" +
HelpExampleCli("listunspent", "") +
"\nLock an unspent transaction\n" +
HelpExampleCli("lockunspent", "false "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nList the locked transactions\n" +
HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n" +
HelpExampleCli("lockunspent", "true "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nAs a json rpc call\n" +
HelpExampleRpc("lockunspent", "false, "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\""));
}
LOCK2(cs_main, pwallet->cs_wallet);
if (request.params.size() == 1) {
RPCTypeCheck(request.params, {UniValue::VBOOL});
} else {
RPCTypeCheck(request.params, {UniValue::VBOOL, UniValue::VARR});
}
bool fUnlock = request.params[0].get_bool();
if (request.params.size() == 1) {
if (fUnlock) {
pwallet->UnlockAllCoins();
}
return true;
}
UniValue outputs = request.params[1].get_array();
for (size_t idx = 0; idx < outputs.size(); idx++) {
const UniValue &output = outputs[idx];
if (!output.isObject()) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected object");
}
const UniValue &o = output.get_obj();
RPCTypeCheckObj(o,
{
{"txid", UniValueType(UniValue::VSTR)},
{"vout", UniValueType(UniValue::VNUM)},
});
std::string txid = find_value(o, "txid").get_str();
if (!IsHex(txid)) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, expected hex txid");
}
int nOutput = find_value(o, "vout").get_int();
if (nOutput < 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"Invalid parameter, vout must be positive");
}
COutPoint outpt(uint256S(txid), nOutput);
if (fUnlock) {
pwallet->UnlockCoin(outpt);
} else {
pwallet->LockCoin(outpt);
}
}
return true;
}
static UniValue listlockunspent(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"listlockunspent\n"
"\nReturns list of temporarily unspendable outputs.\n"
"See the lockunspent call to lock and unlock transactions for "
"spending.\n"
"\nResult:\n"
"[\n"
" {\n"
" \"txid\" : \"transactionid\", (string) The transaction id "
"locked\n"
" \"vout\" : n (numeric) The vout value\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples:\n"
"\nList the unspent transactions\n" +
HelpExampleCli("listunspent", "") +
"\nLock an unspent transaction\n" +
HelpExampleCli("lockunspent", "false "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nList the locked transactions\n" +
HelpExampleCli("listlockunspent", "") +
"\nUnlock the transaction again\n" +
HelpExampleCli("lockunspent", "true "
"\"[{\\\"txid\\\":"
"\\\"a08e6907dbbd3d809776dbfc5d82e371"
"b764ed838b5655e72f463568df1aadf0\\\""
",\\\"vout\\\":1}]\"") +
"\nAs a json rpc call\n" + HelpExampleRpc("listlockunspent", ""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
std::vector<COutPoint> vOutpts;
pwallet->ListLockedCoins(vOutpts);
UniValue ret(UniValue::VARR);
for (COutPoint &output : vOutpts) {
UniValue o(UniValue::VOBJ);
o.push_back(Pair("txid", output.GetTxId().GetHex()));
o.push_back(Pair("vout", int(output.GetN())));
ret.push_back(o);
}
return ret;
}
static UniValue settxfee(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 1) {
throw std::runtime_error(
"settxfee amount\n"
"\nSet the transaction fee per kB. Overwrites the paytxfee "
"parameter.\n"
"\nArguments:\n"
"1. amount (numeric or string, required) The transaction "
"fee in " +
CURRENCY_UNIT +
"/kB\n"
"\nResult\n"
"true|false (boolean) Returns true if successful\n"
"\nExamples:\n" +
HelpExampleCli("settxfee", "0.00001") +
HelpExampleRpc("settxfee", "0.00001"));
}
LOCK2(cs_main, pwallet->cs_wallet);
// Amount
Amount nAmount = AmountFromValue(request.params[0]);
payTxFee = CFeeRate(nAmount, 1000);
return true;
}
static UniValue getwalletinfo(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getwalletinfo\n"
"Returns an object containing various wallet state info.\n"
"\nResult:\n"
"{\n"
" \"walletname\": xxxxx, (string) the wallet name\n"
" \"walletversion\": xxxxx, (numeric) the wallet "
"version\n"
" \"balance\": xxxxxxx, (numeric) the total "
"confirmed balance of the wallet in " +
CURRENCY_UNIT + "\n"
" \"unconfirmed_balance\": xxx, (numeric) "
"the total unconfirmed balance of the wallet in " +
CURRENCY_UNIT + "\n"
" \"immature_balance\": xxxxxx, (numeric) "
"the total immature balance of the wallet in " +
CURRENCY_UNIT +
"\n"
" \"txcount\": xxxxxxx, (numeric) the total number "
"of transactions in the wallet\n"
" \"keypoololdest\": xxxxxx, (numeric) the timestamp "
"(seconds since Unix epoch) of the oldest pre-generated key in the "
"key pool\n"
" \"keypoolsize\": xxxx, (numeric) how many new keys "
"are pre-generated (only counts external keys)\n"
" \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys "
"are pre-generated for internal use (used for change outputs, only "
"appears if the wallet is using this feature, otherwise external "
"keys are used)\n"
" \"unlocked_until\": ttt, (numeric) the 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"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getwalletinfo", "") +
HelpExampleRpc("getwalletinfo", ""));
}
ObserveSafeMode();
LOCK2(cs_main, pwallet->cs_wallet);
UniValue obj(UniValue::VOBJ);
size_t kpExternalSize = pwallet->KeypoolCountExternalKeys();
obj.push_back(Pair("walletname", pwallet->GetName()));
obj.push_back(Pair("walletversion", pwallet->GetVersion()));
obj.push_back(Pair("balance", ValueFromAmount(pwallet->GetBalance())));
obj.push_back(Pair("unconfirmed_balance",
ValueFromAmount(pwallet->GetUnconfirmedBalance())));
obj.push_back(Pair("immature_balance",
ValueFromAmount(pwallet->GetImmatureBalance())));
obj.push_back(Pair("txcount", (int)pwallet->mapWallet.size()));
obj.push_back(Pair("keypoololdest", pwallet->GetOldestKeyPoolTime()));
obj.push_back(Pair("keypoolsize", (int64_t)kpExternalSize));
CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID;
if (!masterKeyID.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) {
obj.push_back(
Pair("keypoolsize_hd_internal",
int64_t(pwallet->GetKeyPoolSize() - kpExternalSize)));
}
if (pwallet->IsCrypted()) {
obj.push_back(Pair("unlocked_until", pwallet->nRelockTime));
}
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
if (!masterKeyID.IsNull()) {
obj.push_back(Pair("hdmasterkeyid", masterKeyID.GetHex()));
}
return obj;
}
static UniValue listwallets(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"listwallets\n"
"Returns a list of currently loaded wallets.\n"
"For full information on the wallet, use \"getwalletinfo\"\n"
"\nResult:\n"
"[ (json array of strings)\n"
" \"walletname\" (string) the wallet name\n"
" ...\n"
"]\n"
"\nExamples:\n" +
HelpExampleCli("listwallets", "") +
HelpExampleRpc("listwallets", ""));
}
UniValue obj(UniValue::VARR);
for (CWalletRef pwallet : vpwallets) {
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
LOCK(pwallet->cs_wallet);
obj.push_back(pwallet->GetName());
}
return obj;
}
static UniValue resendwallettransactions(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"resendwallettransactions\n"
"Immediately re-broadcast unconfirmed wallet transactions to all "
"peers.\n"
"Intended only for testing; the wallet code periodically "
"re-broadcasts\n"
"automatically.\n"
"Returns an RPC error if -walletbroadcast is set to false.\n"
"Returns array of transaction ids that were re-broadcast.\n");
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
LOCK2(cs_main, pwallet->cs_wallet);
if (!pwallet->GetBroadcastTransactions()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction "
"broadcasting is disabled with "
"-walletbroadcast");
}
std::vector<uint256> txids =
pwallet->ResendWalletTransactionsBefore(GetTime(), g_connman.get());
UniValue result(UniValue::VARR);
for (const uint256 &txid : txids) {
result.push_back(txid.ToString());
}
return result;
}
static UniValue listunspent(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 5) {
throw std::runtime_error(
"listunspent ( minconf maxconf [\"addresses\",...] "
"[include_unsafe] [query_options])\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"
"\nArguments:\n"
"1. minconf (numeric, optional, default=1) The minimum "
"confirmations to filter\n"
"2. maxconf (numeric, optional, default=9999999) The "
"maximum confirmations to filter\n"
"3. \"addresses\" (string) A json array of bitcoin addresses "
"to filter\n"
" [\n"
" \"address\" (string) bitcoin address\n"
" ,...\n"
" ]\n"
"4. include_unsafe (bool, optional, default=true) Include outputs "
"that are not safe to spend\n"
" See description of \"safe\" attribute below.\n"
"5. query_options (json, optional) JSON with query options\n"
" {\n"
" \"minimumAmount\" (numeric or string, default=0) Minimum "
"value of each UTXO in " +
CURRENCY_UNIT + "\n"
" \"maximumAmount\" (numeric or string, "
"default=unlimited) Maximum value of each UTXO "
"in " +
CURRENCY_UNIT + "\n"
" \"maximumCount\" (numeric or string, "
"default=unlimited) Maximum number of UTXOs\n"
" \"minimumSumAmount\" (numeric or string, "
"default=unlimited) Minimum sum value of all UTXOs "
"in " +
CURRENCY_UNIT +
"\n"
" }\n"
"\nResult\n"
"[ (array of json object)\n"
" {\n"
" \"txid\" : \"txid\", (string) the transaction id \n"
" \"vout\" : n, (numeric) the vout value\n"
" \"address\" : \"address\", (string) the bitcoin address\n"
" \"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 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"
" \"solvable\" : xxx, (bool) Whether we know how to "
"spend this output, ignoring the lack of keys\n"
" \"safe\" : xxx (bool) Whether this output is "
"considered safe to spend. Unconfirmed transactions\n"
" from outside keys are considered "
"unsafe and are not eligible for spending by\n"
" fundrawtransaction and "
"sendtoaddress.\n"
" }\n"
" ,...\n"
"]\n"
"\nExamples\n" +
HelpExampleCli("listunspent", "") +
HelpExampleCli("listunspent",
"6 9999999 "
"\"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\","
"\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") +
HelpExampleRpc("listunspent",
"6, 9999999 "
"\"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\","
"\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") +
HelpExampleCli(
"listunspent",
"6 9999999 '[]' true '{ \"minimumAmount\": 0.005 }'") +
HelpExampleRpc(
"listunspent",
"6, 9999999, [] , true, { \"minimumAmount\": 0.005 } "));
}
ObserveSafeMode();
int nMinDepth = 1;
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 && !request.params[1].isNull()) {
RPCTypeCheckArgument(request.params[1], UniValue::VNUM);
nMaxDepth = request.params[1].get_int();
}
std::set<CTxDestination> destinations;
if (request.params.size() > 2 && !request.params[2].isNull()) {
RPCTypeCheckArgument(request.params[2], UniValue::VARR);
UniValue inputs = request.params[2].get_array();
for (size_t idx = 0; idx < inputs.size(); idx++) {
const UniValue &input = inputs[idx];
CTxDestination dest =
DecodeDestination(input.get_str(), config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
std::string("Invalid Bitcoin address: ") +
input.get_str());
}
if (!destinations.insert(dest).second) {
throw JSONRPCError(
RPC_INVALID_PARAMETER,
std::string("Invalid parameter, duplicated address: ") +
input.get_str());
}
}
}
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();
}
Amount nMinimumAmount = Amount::zero();
Amount nMaximumAmount = MAX_MONEY;
Amount nMinimumSumAmount = MAX_MONEY;
uint64_t nMaximumCount = 0;
if (!request.params[4].isNull()) {
const UniValue &options = request.params[4].get_obj();
if (options.exists("minimumAmount")) {
nMinimumAmount = AmountFromValue(options["minimumAmount"]);
}
if (options.exists("maximumAmount")) {
nMaximumAmount = AmountFromValue(options["maximumAmount"]);
}
if (options.exists("minimumSumAmount")) {
nMinimumSumAmount = AmountFromValue(options["minimumSumAmount"]);
}
if (options.exists("maximumCount")) {
nMaximumCount = options["maximumCount"].get_int64();
}
}
UniValue results(UniValue::VARR);
std::vector<COutput> vecOutputs;
assert(pwallet != nullptr);
LOCK2(cs_main, pwallet->cs_wallet);
pwallet->AvailableCoins(vecOutputs, !include_unsafe, nullptr,
nMinimumAmount, nMaximumAmount, nMinimumSumAmount,
nMaximumCount, nMinDepth, nMaxDepth);
for (const COutput &out : vecOutputs) {
CTxDestination address;
const CScript &scriptPubKey = out.tx->tx->vout[out.i].scriptPubKey;
bool fValidAddress = ExtractDestination(scriptPubKey, address);
if (destinations.size() &&
(!fValidAddress || !destinations.count(address))) {
continue;
}
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("txid", out.tx->GetId().GetHex()));
entry.push_back(Pair("vout", out.i));
if (fValidAddress) {
entry.push_back(Pair("address", EncodeDestination(address)));
if (pwallet->mapAddressBook.count(address)) {
entry.push_back(
Pair("account", pwallet->mapAddressBook[address].name));
}
if (scriptPubKey.IsPayToScriptHash()) {
const CScriptID &hash = boost::get<CScriptID>(address);
CScript redeemScript;
if (pwallet->GetCScript(hash, redeemScript)) {
entry.push_back(
Pair("redeemScript",
HexStr(redeemScript.begin(), redeemScript.end())));
}
}
}
entry.push_back(Pair("scriptPubKey",
HexStr(scriptPubKey.begin(), scriptPubKey.end())));
entry.push_back(
Pair("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue)));
entry.push_back(Pair("confirmations", out.nDepth));
entry.push_back(Pair("spendable", out.fSpendable));
entry.push_back(Pair("solvable", out.fSolvable));
entry.push_back(Pair("safe", out.fSafe));
results.push_back(entry);
}
return results;
}
static UniValue fundrawtransaction(const Config &config,
const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::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 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"
"Note that all inputs selected must be of standard form and P2SH "
"scripts must be\n"
"in the wallet using importaddress or addmultisigaddress (to "
"calculate fees).\n"
"You can see whether this is the case by checking the \"solvable\" "
"field in the listunspent output.\n"
"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"
" {\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"
"{\n"
" \"hex\": \"value\", (string) The resulting raw "
"transaction (hex-encoded string)\n"
" \"fee\": n, (numeric) Fee in " +
CURRENCY_UNIT + " the resulting transaction pays\n"
" \"changepos\": n (numeric) The "
"position of the added change output, or -1\n"
"}\n"
"\nExamples:\n"
"\nCreate a transaction with no inputs\n" +
HelpExampleCli("createrawtransaction",
"\"[]\" \"{\\\"myaddress\\\":0.01}\"") +
"\nAdd sufficient unsigned inputs to meet the output value\n" +
HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") +
"\nSign the transaction\n" +
HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") +
"\nSend the transaction\n" +
HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\""));
}
ObserveSafeMode();
RPCTypeCheck(request.params, {UniValue::VSTR});
CTxDestination changeAddress = CNoDestination();
int changePosition = -1;
bool includeWatching = false;
bool lockUnspents = false;
bool reserveChangeKey = true;
CFeeRate feeRate = CFeeRate(Amount::zero());
bool overrideEstimatedFeerate = false;
UniValue subtractFeeFromOutputs;
std::set<int> setSubtractFeeFromOutputs;
if (request.params.size() > 1) {
if (request.params[1].type() == UniValue::VBOOL) {
// backward compatibility bool only fallback
includeWatching = request.params[1].get_bool();
} else {
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
UniValue options = request.params[1];
RPCTypeCheckObj(
options,
{
{"changeAddress", UniValueType(UniValue::VSTR)},
{"changePosition", UniValueType(UniValue::VNUM)},
{"includeWatching", UniValueType(UniValue::VBOOL)},
{"lockUnspents", UniValueType(UniValue::VBOOL)},
{"reserveChangeKey", UniValueType(UniValue::VBOOL)},
// will be checked below
{"feeRate", UniValueType()},
{"subtractFeeFromOutputs", UniValueType(UniValue::VARR)},
},
true, true);
if (options.exists("changeAddress")) {
CTxDestination dest =
DecodeDestination(options["changeAddress"].get_str(),
config.GetChainParams());
if (!IsValidDestination(dest)) {
throw JSONRPCError(
RPC_INVALID_ADDRESS_OR_KEY,
"changeAddress must be a valid bitcoin address");
}
changeAddress = dest;
}
if (options.exists("changePosition")) {
changePosition = options["changePosition"].get_int();
}
if (options.exists("includeWatching")) {
includeWatching = options["includeWatching"].get_bool();
}
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();
}
}
}
// parse hex string from parameter
CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
if (tx.vout.size() == 0) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"TX must have at least one output");
}
if (changePosition != -1 &&
(changePosition < 0 || (unsigned int)changePosition > tx.vout.size())) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"changePosition out of bounds");
}
for (size_t 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);
}
Amount nFeeOut;
std::string strFailReason;
if (!pwallet->FundTransaction(
tx, nFeeOut, overrideEstimatedFeerate, feeRate, changePosition,
strFailReason, includeWatching, lockUnspents,
setSubtractFeeFromOutputs, reserveChangeKey, changeAddress)) {
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
UniValue result(UniValue::VOBJ);
result.push_back(Pair("hex", EncodeHexTx(CTransaction(tx))));
result.push_back(Pair("changepos", changePosition));
result.push_back(Pair("fee", ValueFromAmount(nFeeOut)));
return result;
}
static UniValue generate(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"generate nblocks ( maxtries )\n"
"\nMine up to nblocks blocks immediately (before the RPC call "
"returns) to an address in the wallet.\n"
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated "
"immediately.\n"
"2. maxtries (numeric, optional) How many iterations to try "
"(default = 1000000).\n"
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
"\nGenerate 11 blocks\n" +
HelpExampleCli("generate", "11"));
}
int num_generate = request.params[0].get_int();
uint64_t max_tries = 1000000;
if (request.params.size() > 1 && !request.params[1].isNull()) {
max_tries = request.params[1].get_int();
}
std::shared_ptr<CReserveScript> coinbase_script;
pwallet->GetScriptForMining(coinbase_script);
// If the keypool is exhausted, no script is returned at all. Catch this.
if (!coinbase_script) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
// throw an error if no script was provided
if (coinbase_script->reserveScript.empty()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "No coinbase script available");
}
return generateBlocks(config, coinbase_script, num_generate, max_tries,
true);
}
UniValue rescanblockchain(const Config &config, const JSONRPCRequest &request) {
CWallet *const pwallet = GetWalletForJSONRPCRequest(request);
if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) {
return NullUniValue;
}
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"rescanblockchain (\"start_height\") (\"stop_height\")\n"
"\nRescan the local blockchain for wallet related transactions.\n"
"\nArguments:\n"
"1. \"start_height\" (numeric, optional) block height where the "
"rescan should start\n"
"2. \"stop_height\" (numeric, optional) the last block height "
"that should be scanned\n"
"\nResult:\n"
"{\n"
" \"start_height\" (numeric) The block height where the "
"rescan has started. If omitted, rescan started from the genesis "
"block.\n"
" \"stop_height\" (numeric) The height of the last rescanned "
"block. If omitted, rescan stopped at the chain tip.\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("rescanblockchain", "100000 120000") +
HelpExampleRpc("rescanblockchain", "100000 120000"));
}
LOCK2(cs_main, pwallet->cs_wallet);
CBlockIndex *pindexStart = chainActive.Genesis();
CBlockIndex *pindexStop = nullptr;
if (!request.params[0].isNull()) {
pindexStart = chainActive[request.params[0].get_int()];
if (!pindexStart) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid start_height");
}
}
if (!request.params[1].isNull()) {
pindexStop = chainActive[request.params[1].get_int()];
if (!pindexStop) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid stop_height");
} else if (pindexStop->nHeight < pindexStart->nHeight) {
throw JSONRPCError(RPC_INVALID_PARAMETER,
"stop_height must be greater then start_height");
}
}
// We can't rescan beyond non-pruned blocks, stop and throw an error
if (fPruneMode) {
CBlockIndex *block = pindexStop ? pindexStop : chainActive.Tip();
while (block && block->nHeight >= pindexStart->nHeight) {
if (!block->nStatus.hasData()) {
throw JSONRPCError(RPC_MISC_ERROR,
"Can't rescan beyond pruned data. Use RPC "
"call getblockchaininfo to determine your "
"pruned height.");
}
block = block->pprev;
}
}
CBlockIndex *stopBlock =
pwallet->ScanForWalletTransactions(pindexStart, pindexStop, true);
if (!stopBlock) {
if (pwallet->IsAbortingRescan()) {
throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted.");
}
// if we got a nullptr returned, ScanForWalletTransactions did rescan up
// to the requested stopindex
stopBlock = pindexStop ? pindexStop : chainActive.Tip();
} else {
throw JSONRPCError(RPC_MISC_ERROR,
"Rescan failed. Potentially corrupted data files.");
}
UniValue response(UniValue::VOBJ);
response.pushKV("start_height", pindexStart->nHeight);
response.pushKV("stop_height", stopBlock->nHeight);
return response;
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ------------------- ------------------------ ---------------------- ----------
{ "rawtransactions", "fundrawtransaction", fundrawtransaction, {"hexstring","options"} },
{ "hidden", "resendwallettransactions", resendwallettransactions, {} },
{ "wallet", "abandontransaction", abandontransaction, {"txid"} },
{ "wallet", "addmultisigaddress", addmultisigaddress, {"nrequired","keys","account"} },
{ "wallet", "backupwallet", backupwallet, {"destination"} },
{ "wallet", "encryptwallet", encryptwallet, {"passphrase"} },
{ "wallet", "getaccountaddress", getaccountaddress, {"account"} },
{ "wallet", "getaccount", getaccount, {"address"} },
{ "wallet", "getaddressesbyaccount", getaddressesbyaccount, {"account"} },
{ "wallet", "getbalance", getbalance, {"account","minconf","include_watchonly"} },
{ "wallet", "getnewaddress", getnewaddress, {"account"} },
{ "wallet", "getrawchangeaddress", getrawchangeaddress, {} },
{ "wallet", "getreceivedbyaccount", getreceivedbyaccount, {"account","minconf"} },
{ "wallet", "getreceivedbyaddress", getreceivedbyaddress, {"address","minconf"} },
{ "wallet", "gettransaction", gettransaction, {"txid","include_watchonly"} },
{ "wallet", "getunconfirmedbalance", getunconfirmedbalance, {} },
{ "wallet", "getwalletinfo", getwalletinfo, {} },
{ "wallet", "keypoolrefill", keypoolrefill, {"newsize"} },
{ "wallet", "listaccounts", listaccounts, {"minconf","include_watchonly"} },
{ "wallet", "listaddressgroupings", listaddressgroupings, {} },
{ "wallet", "listlockunspent", listlockunspent, {} },
{ "wallet", "listreceivedbyaccount", listreceivedbyaccount, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listreceivedbyaddress", listreceivedbyaddress, {"minconf","include_empty","include_watchonly"} },
{ "wallet", "listsinceblock", listsinceblock, {"blockhash","target_confirmations","include_watchonly"} },
{ "wallet", "listtransactions", listtransactions, {"account","count","skip","include_watchonly"} },
{ "wallet", "listunspent", listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} },
{ "wallet", "listwallets", listwallets, {} },
{ "wallet", "lockunspent", lockunspent, {"unlock","transactions"} },
{ "wallet", "move", movecmd, {"fromaccount","toaccount","amount","minconf","comment"} },
{ "wallet", "rescanblockchain", rescanblockchain, {"start_height", "stop_height"} },
{ "wallet", "sendfrom", sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} },
{ "wallet", "sendmany", sendmany, {"fromaccount","amounts","minconf","comment","subtractfeefrom"} },
{ "wallet", "sendtoaddress", sendtoaddress, {"address","amount","comment","comment_to","subtractfeefromamount"} },
{ "wallet", "setaccount", setaccount, {"address","account"} },
{ "wallet", "settxfee", settxfee, {"amount"} },
{ "wallet", "signmessage", signmessage, {"address","message"} },
{ "wallet", "walletlock", walletlock, {} },
{ "wallet", "walletpassphrasechange", walletpassphrasechange, {"oldpassphrase","newpassphrase"} },
{ "wallet", "walletpassphrase", walletpassphrase, {"passphrase","timeout"} },
{ "generating", "generate", generate, {"nblocks","maxtries"} },
};
// clang-format on
void RegisterWalletRPCCommands(CRPCTable &t) {
if (gArgs.GetBoolArg("-disablewallet", false)) {
return;
}
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) {
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index a63011321..6508a199c 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1,4261 +1,4266 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// 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.
#include "wallet/wallet.h"
#include "chain.h"
#include "checkpoints.h"
#include "config.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
#include "dstencode.h"
#include "fs.h"
#include "init.h"
#include "key.h"
#include "keystore.h"
#include "net.h"
#include "policy/policy.h"
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "scheduler.h"
#include "script/script.h"
#include "script/sighashtype.h"
#include "script/sign.h"
#include "timedata.h"
#include "txmempool.h"
#include "ui_interface.h"
#include "util.h"
#include "utilmoneystr.h"
#include "validation.h"
#include "wallet/coincontrol.h"
#include "wallet/fees.h"
#include "wallet/finaltx.h"
#include <boost/algorithm/string/replace.hpp>
#include <cassert>
std::vector<CWalletRef> vpwallets;
/** Transaction fee set by the user */
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
const char *DEFAULT_WALLET_DAT = "wallet.dat";
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
/**
* If fee estimation does not have enough data to provide estimates, use this
* fee instead. Has no effect if not using fee estimation.
* Override with -fallbackfee
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
const uint256 CMerkleTx::ABANDON_HASH(uint256S(
"0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
*
* @{
*/
struct CompareValueOnly {
bool operator()(
const std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> &t1,
const std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> &t2)
const {
return t1.first < t2.first;
}
};
std::string COutput::ToString() const {
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetId().ToString(), i,
nDepth, FormatMoney(tx->tx->vout[i].nValue));
}
class CAffectedKeysVisitor : public boost::static_visitor<void> {
private:
const CKeyStore &keystore;
std::vector<CKeyID> &vKeys;
public:
CAffectedKeysVisitor(const CKeyStore &keystoreIn,
std::vector<CKeyID> &vKeysIn)
: keystore(keystoreIn), vKeys(vKeysIn) {}
void Process(const CScript &script) {
txnouttype type;
std::vector<CTxDestination> vDest;
int nRequired;
if (ExtractDestinations(script, type, vDest, nRequired)) {
for (const CTxDestination &dest : vDest) {
boost::apply_visitor(*this, dest);
}
}
}
void operator()(const CKeyID &keyId) {
if (keystore.HaveKey(keyId)) {
vKeys.push_back(keyId);
}
}
void operator()(const CScriptID &scriptId) {
CScript script;
if (keystore.GetCScript(scriptId, script)) {
Process(script);
}
}
void operator()(const CNoDestination &none) {}
};
const CWalletTx *CWallet::GetWalletTx(const TxId &txid) const {
LOCK(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid);
if (it == mapWallet.end()) {
return nullptr;
}
return &(it->second);
}
CPubKey CWallet::GenerateNewKey(CWalletDB &walletdb, bool internal) {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
// default to compressed public keys if we want 0.6.0 wallets
bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY);
CKey secret;
// Create new metadata
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// use HD key derivation if HD was enabled during wallet creation
if (IsHDEnabled()) {
DeriveNewChildKey(
walletdb, metadata, secret,
(CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false));
} else {
secret.MakeNewKey(fCompressed);
}
// Compressed public keys were introduced in version 0.6.0
if (fCompressed) {
SetMinVersion(FEATURE_COMPRPUBKEY);
}
CPubKey pubkey = secret.GetPubKey();
assert(secret.VerifyPubKey(pubkey));
mapKeyMetadata[pubkey.GetID()] = metadata;
UpdateTimeFirstKey(nCreationTime);
if (!AddKeyPubKeyWithDB(walletdb, secret, pubkey)) {
throw std::runtime_error(std::string(__func__) + ": AddKey failed");
}
return pubkey;
}
void CWallet::DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata &metadata,
CKey &secret, bool internal) {
// for now we use a fixed keypath scheme of m/0'/0'/k
// master key seed (256bit)
CKey key;
// hd master key
CExtKey masterKey;
// key at m/0'
CExtKey accountKey;
// key at m/0'/0' (external) or m/0'/1' (internal)
CExtKey chainChildKey;
// key at m/0'/0'/<n>'
CExtKey childKey;
// try to get the master key
if (!GetKey(hdChain.masterKeyID, key)) {
throw std::runtime_error(std::string(__func__) +
": Master key not found");
}
masterKey.SetMaster(key.begin(), key.size());
// derive m/0'
// use hardened derivation (child keys >= 0x80000000 are hardened after
// bip32)
masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);
// derive m/0'/0' (external chain) OR m/0'/1' (internal chain)
assert(internal ? CanSupportFeature(FEATURE_HD_SPLIT) : true);
accountKey.Derive(chainChildKey,
BIP32_HARDENED_KEY_LIMIT + (internal ? 1 : 0));
// derive child key at next index, skip keys already known to the wallet
do {
// always derive hardened keys
// childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened
// child-index-range
// example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649
if (internal) {
chainChildKey.Derive(childKey,
hdChain.nInternalChainCounter |
BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/1'/" +
std::to_string(hdChain.nInternalChainCounter) +
"'";
hdChain.nInternalChainCounter++;
} else {
chainChildKey.Derive(childKey,
hdChain.nExternalChainCounter |
BIP32_HARDENED_KEY_LIMIT);
metadata.hdKeypath = "m/0'/0'/" +
std::to_string(hdChain.nExternalChainCounter) +
"'";
hdChain.nExternalChainCounter++;
}
} while (HaveKey(childKey.key.GetPubKey().GetID()));
secret = childKey.key;
metadata.hdMasterKeyID = hdChain.masterKeyID;
// update the chain model in the database
if (!walletdb.WriteHDChain(hdChain)) {
throw std::runtime_error(std::string(__func__) +
": Writing HD chain model failed");
}
}
bool CWallet::AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey &secret,
const CPubKey &pubkey) {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
// CCryptoKeyStore has no concept of wallet databases, but calls
// AddCryptedKey
// which is overridden below. To avoid flushes, the database handle is
// tunneled through to it.
bool needsDB = !pwalletdbEncryption;
if (needsDB) {
pwalletdbEncryption = &walletdb;
}
if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) {
if (needsDB) {
pwalletdbEncryption = nullptr;
}
return false;
}
if (needsDB) {
pwalletdbEncryption = nullptr;
}
// Check if we need to remove from watch-only.
CScript script;
script = GetScriptForDestination(pubkey.GetID());
if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
}
script = GetScriptForRawPubKey(pubkey);
if (HaveWatchOnly(script)) {
RemoveWatchOnly(script);
}
if (IsCrypted()) {
return true;
}
return walletdb.WriteKey(pubkey, secret.GetPrivKey(),
mapKeyMetadata[pubkey.GetID()]);
}
bool CWallet::AddKeyPubKey(const CKey &secret, const CPubKey &pubkey) {
CWalletDB walletdb(*dbw);
return CWallet::AddKeyPubKeyWithDB(walletdb, secret, pubkey);
}
bool CWallet::AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret) {
if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) {
return false;
}
LOCK(cs_wallet);
if (pwalletdbEncryption) {
return pwalletdbEncryption->WriteCryptedKey(
vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]);
}
return CWalletDB(*dbw).WriteCryptedKey(vchPubKey, vchCryptedSecret,
mapKeyMetadata[vchPubKey.GetID()]);
}
bool CWallet::LoadKeyMetadata(const CTxDestination &keyID,
const CKeyMetadata &meta) {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
UpdateTimeFirstKey(meta.nCreateTime);
mapKeyMetadata[keyID] = meta;
return true;
}
bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret) {
return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);
}
/**
* Update wallet first key creation time. This should be called whenever keys
* are added to the wallet, with the oldest key creation time.
*/
void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) {
AssertLockHeld(cs_wallet);
if (nCreateTime <= 1) {
// Cannot determine birthday information, so set the wallet birthday to
// the beginning of time.
nTimeFirstKey = 1;
} else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {
nTimeFirstKey = nCreateTime;
}
}
bool CWallet::AddCScript(const CScript &redeemScript) {
if (!CCryptoKeyStore::AddCScript(redeemScript)) {
return false;
}
return CWalletDB(*dbw).WriteCScript(Hash160(redeemScript), redeemScript);
}
bool CWallet::LoadCScript(const CScript &redeemScript) {
/**
* A sanity check was added in pull #3843 to avoid adding redeemScripts that
* never can be redeemed. However, old wallets may still contain these. Do
* not add them to the wallet and warn.
*/
if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) {
std::string strAddr = EncodeDestination(CScriptID(redeemScript));
LogPrintf("%s: Warning: This wallet contains a redeemScript of size %i "
"which exceeds maximum size %i thus can never be redeemed. "
"Do not use address %s.\n",
__func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE,
strAddr);
return true;
}
return CCryptoKeyStore::AddCScript(redeemScript);
}
bool CWallet::AddWatchOnly(const CScript &dest) {
if (!CCryptoKeyStore::AddWatchOnly(dest)) {
return false;
}
const CKeyMetadata &meta = mapKeyMetadata[CScriptID(dest)];
UpdateTimeFirstKey(meta.nCreateTime);
NotifyWatchonlyChanged(true);
return CWalletDB(*dbw).WriteWatchOnly(dest, meta);
}
bool CWallet::AddWatchOnly(const CScript &dest, int64_t nCreateTime) {
mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime;
return AddWatchOnly(dest);
}
bool CWallet::RemoveWatchOnly(const CScript &dest) {
AssertLockHeld(cs_wallet);
if (!CCryptoKeyStore::RemoveWatchOnly(dest)) {
return false;
}
if (!HaveWatchOnly()) {
NotifyWatchonlyChanged(false);
}
return CWalletDB(*dbw).EraseWatchOnly(dest);
}
bool CWallet::LoadWatchOnly(const CScript &dest) {
return CCryptoKeyStore::AddWatchOnly(dest);
}
bool CWallet::Unlock(const SecureString &strWalletPassphrase) {
CCrypter crypter;
CKeyingMaterial _vMasterKey;
LOCK(cs_wallet);
for (const MasterKeyMap::value_type &pMasterKey : mapMasterKeys) {
if (!crypter.SetKeyFromPassphrase(
strWalletPassphrase, pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod)) {
return false;
}
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) {
// try another master key
continue;
}
if (CCryptoKeyStore::Unlock(_vMasterKey)) {
return true;
}
}
return false;
}
bool CWallet::ChangeWalletPassphrase(
const SecureString &strOldWalletPassphrase,
const SecureString &strNewWalletPassphrase) {
bool fWasLocked = IsLocked();
LOCK(cs_wallet);
Lock();
CCrypter crypter;
CKeyingMaterial _vMasterKey;
for (MasterKeyMap::value_type &pMasterKey : mapMasterKeys) {
if (!crypter.SetKeyFromPassphrase(
strOldWalletPassphrase, pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod)) {
return false;
}
if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, _vMasterKey)) {
return false;
}
if (CCryptoKeyStore::Unlock(_vMasterKey)) {
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase,
pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations =
pMasterKey.second.nDeriveIterations *
(100 / ((double)(GetTimeMillis() - nStartTime)));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strNewWalletPassphrase,
pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod);
pMasterKey.second.nDeriveIterations =
(pMasterKey.second.nDeriveIterations +
pMasterKey.second.nDeriveIterations * 100 /
double(GetTimeMillis() - nStartTime)) /
2;
if (pMasterKey.second.nDeriveIterations < 25000) {
pMasterKey.second.nDeriveIterations = 25000;
}
LogPrintf(
"Wallet passphrase changed to an nDeriveIterations of %i\n",
pMasterKey.second.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(
strNewWalletPassphrase, pMasterKey.second.vchSalt,
pMasterKey.second.nDeriveIterations,
pMasterKey.second.nDerivationMethod)) {
return false;
}
if (!crypter.Encrypt(_vMasterKey,
pMasterKey.second.vchCryptedKey)) {
return false;
}
CWalletDB(*dbw).WriteMasterKey(pMasterKey.first, pMasterKey.second);
if (fWasLocked) {
Lock();
}
return true;
}
}
return false;
}
void CWallet::SetBestChain(const CBlockLocator &loc) {
CWalletDB walletdb(*dbw);
walletdb.WriteBestBlock(loc);
}
bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB *pwalletdbIn,
bool fExplicit) {
// nWalletVersion
LOCK(cs_wallet);
if (nWalletVersion >= nVersion) {
return true;
}
// When doing an explicit upgrade, if we pass the max version permitted,
// upgrade all the way.
if (fExplicit && nVersion > nWalletMaxVersion) {
nVersion = FEATURE_LATEST;
}
nWalletVersion = nVersion;
if (nVersion > nWalletMaxVersion) {
nWalletMaxVersion = nVersion;
}
CWalletDB *pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(*dbw);
if (nWalletVersion > 40000) {
pwalletdb->WriteMinVersion(nWalletVersion);
}
if (!pwalletdbIn) {
delete pwalletdb;
}
return true;
}
bool CWallet::SetMaxVersion(int nVersion) {
// nWalletVersion, nWalletMaxVersion
LOCK(cs_wallet);
// Cannot downgrade below current version
if (nWalletVersion > nVersion) {
return false;
}
nWalletMaxVersion = nVersion;
return true;
}
std::set<TxId> CWallet::GetConflicts(const TxId &txid) const {
std::set<TxId> result;
AssertLockHeld(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator it = mapWallet.find(txid);
if (it == mapWallet.end()) {
return result;
}
const CWalletTx &wtx = it->second;
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;
for (const CTxIn &txin : wtx.tx->vin) {
if (mapTxSpends.count(txin.prevout) <= 1) {
// No conflict if zero or one spends.
continue;
}
range = mapTxSpends.equal_range(txin.prevout);
for (TxSpends::const_iterator _it = range.first; _it != range.second;
++_it) {
result.insert(_it->second);
}
}
return result;
}
bool CWallet::HasWalletSpend(const TxId &txid) const {
AssertLockHeld(cs_wallet);
auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));
return (iter != mapTxSpends.end() && iter->first.GetTxId() == txid);
}
void CWallet::Flush(bool shutdown) {
dbw->Flush(shutdown);
}
void CWallet::SyncMetaData(
std::pair<TxSpends::iterator, TxSpends::iterator> range) {
// We want all the wallet transactions in range to have the same metadata as
// the oldest (smallest nOrderPos).
// So: find smallest nOrderPos:
int nMinOrderPos = std::numeric_limits<int>::max();
const CWalletTx *copyFrom = nullptr;
for (TxSpends::iterator it = range.first; it != range.second; ++it) {
const TxId &txid = it->second;
int n = mapWallet[txid].nOrderPos;
if (n < nMinOrderPos) {
nMinOrderPos = n;
copyFrom = &mapWallet[txid];
}
}
// Now copy data from copyFrom to rest:
for (TxSpends::iterator it = range.first; it != range.second; ++it) {
const TxId &txid = it->second;
CWalletTx *copyTo = &mapWallet[txid];
if (copyFrom == copyTo) {
continue;
}
if (!copyFrom->IsEquivalentTo(*copyTo)) {
continue;
}
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
// fTimeReceivedIsTxTime not copied on purpose nTimeReceived not copied
// on purpose.
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
// nOrderPos not copied on purpose cached members not copied on purpose.
}
}
/**
* Outpoint is spent if any non-conflicted transaction, spends it:
*/
bool CWallet::IsSpent(const TxId &txid, uint32_t n) const {
const COutPoint outpoint(txid, n);
std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range =
mapTxSpends.equal_range(outpoint);
for (TxSpends::const_iterator it = range.first; it != range.second; ++it) {
const TxId &wtxid = it->second;
std::map<TxId, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);
if (mit != mapWallet.end()) {
int depth = mit->second.GetDepthInMainChain();
if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) {
// Spent
return true;
}
}
}
return false;
}
void CWallet::AddToSpends(const COutPoint &outpoint, const TxId &wtxid) {
mapTxSpends.insert(std::make_pair(outpoint, wtxid));
std::pair<TxSpends::iterator, TxSpends::iterator> range;
range = mapTxSpends.equal_range(outpoint);
SyncMetaData(range);
}
void CWallet::AddToSpends(const TxId &wtxid) {
assert(mapWallet.count(wtxid));
CWalletTx &thisTx = mapWallet[wtxid];
// Coinbases don't spend anything!
if (thisTx.IsCoinBase()) {
return;
}
for (const CTxIn &txin : thisTx.tx->vin) {
AddToSpends(txin.prevout, wtxid);
}
}
bool CWallet::EncryptWallet(const SecureString &strWalletPassphrase) {
if (IsCrypted()) {
return false;
}
CKeyingMaterial _vMasterKey;
_vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);
GetStrongRandBytes(&_vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);
CMasterKey kMasterKey;
kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);
GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);
CCrypter crypter;
int64_t nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000,
kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations =
2500000 / ((double)(GetTimeMillis() - nStartTime));
nStartTime = GetTimeMillis();
crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt,
kMasterKey.nDeriveIterations,
kMasterKey.nDerivationMethod);
kMasterKey.nDeriveIterations =
(kMasterKey.nDeriveIterations +
kMasterKey.nDeriveIterations * 100 /
((double)(GetTimeMillis() - nStartTime))) /
2;
if (kMasterKey.nDeriveIterations < 25000) {
kMasterKey.nDeriveIterations = 25000;
}
LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n",
kMasterKey.nDeriveIterations);
if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt,
kMasterKey.nDeriveIterations,
kMasterKey.nDerivationMethod)) {
return false;
}
if (!crypter.Encrypt(_vMasterKey, kMasterKey.vchCryptedKey)) {
return false;
}
{
LOCK(cs_wallet);
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
assert(!pwalletdbEncryption);
pwalletdbEncryption = new CWalletDB(*dbw);
if (!pwalletdbEncryption->TxnBegin()) {
delete pwalletdbEncryption;
pwalletdbEncryption = nullptr;
return false;
}
pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);
if (!EncryptKeys(_vMasterKey)) {
pwalletdbEncryption->TxnAbort();
delete pwalletdbEncryption;
// We now probably have half of our keys encrypted in memory, and
// half not... die and let the user reload the unencrypted wallet.
assert(false);
}
// Encryption was introduced in version 0.4.0
SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);
if (!pwalletdbEncryption->TxnCommit()) {
delete pwalletdbEncryption;
// We now have keys encrypted in memory, but not on disk... die to
// avoid confusion and let the user reload the unencrypted wallet.
assert(false);
}
delete pwalletdbEncryption;
pwalletdbEncryption = nullptr;
Lock();
Unlock(strWalletPassphrase);
// If we are using HD, replace the HD master key (seed) with a new one.
if (IsHDEnabled()) {
CKey key;
CPubKey masterPubKey = GenerateNewHDMasterKey();
// preserve the old chains version to not break backward
// compatibility
CHDChain oldChain = GetHDChain();
if (!SetHDMasterKey(masterPubKey, &oldChain)) {
return false;
}
}
NewKeyPool();
Lock();
// Need to completely rewrite the wallet file; if we don't, bdb might
// keep bits of the unencrypted private key in slack space in the
// database file.
dbw->Rewrite();
}
NotifyStatusChanged(this);
return true;
}
DBErrors CWallet::ReorderTransactions() {
LOCK(cs_wallet);
CWalletDB walletdb(*dbw);
// Old wallets didn't have any defined order for transactions. Probably a
// bad idea to change the output of this.
// First: get all CWalletTx and CAccountingEntry into a sorted-by-time
// multimap.
TxItems txByTime;
for (std::map<TxId, CWalletTx>::iterator it = mapWallet.begin();
it != mapWallet.end(); ++it) {
CWalletTx *wtx = &((*it).second);
txByTime.insert(
std::make_pair(wtx->nTimeReceived, TxPair(wtx, nullptr)));
}
std::list<CAccountingEntry> acentries;
walletdb.ListAccountCreditDebit("", acentries);
for (CAccountingEntry &entry : acentries) {
txByTime.insert(std::make_pair(entry.nTime, TxPair(nullptr, &entry)));
}
nOrderPosNext = 0;
std::vector<int64_t> nOrderPosOffsets;
for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) {
CWalletTx *const pwtx = (*it).second.first;
CAccountingEntry *const pacentry = (*it).second.second;
int64_t &nOrderPos =
(pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;
if (nOrderPos == -1) {
nOrderPos = nOrderPosNext++;
nOrderPosOffsets.push_back(nOrderPos);
if (pwtx) {
if (!walletdb.WriteTx(*pwtx)) {
return DB_LOAD_FAIL;
}
} else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo,
*pacentry)) {
return DB_LOAD_FAIL;
}
} else {
int64_t nOrderPosOff = 0;
for (const int64_t &nOffsetStart : nOrderPosOffsets) {
if (nOrderPos >= nOffsetStart) {
++nOrderPosOff;
}
}
nOrderPos += nOrderPosOff;
nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);
if (!nOrderPosOff) {
continue;
}
// Since we're changing the order, write it back.
if (pwtx) {
if (!walletdb.WriteTx(*pwtx)) {
return DB_LOAD_FAIL;
}
} else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo,
*pacentry)) {
return DB_LOAD_FAIL;
}
}
}
walletdb.WriteOrderPosNext(nOrderPosNext);
return DB_LOAD_OK;
}
int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) {
// nOrderPosNext
AssertLockHeld(cs_wallet);
int64_t nRet = nOrderPosNext++;
if (pwalletdb) {
pwalletdb->WriteOrderPosNext(nOrderPosNext);
} else {
CWalletDB(*dbw).WriteOrderPosNext(nOrderPosNext);
}
return nRet;
}
bool CWallet::AccountMove(std::string strFrom, std::string strTo,
const Amount nAmount, std::string strComment) {
CWalletDB walletdb(*dbw);
if (!walletdb.TxnBegin()) {
return false;
}
int64_t nNow = GetAdjustedTime();
// Debit
CAccountingEntry debit;
debit.nOrderPos = IncOrderPosNext(&walletdb);
debit.strAccount = strFrom;
debit.nCreditDebit = -nAmount;
debit.nTime = nNow;
debit.strOtherAccount = strTo;
debit.strComment = strComment;
AddAccountingEntry(debit, &walletdb);
// Credit
CAccountingEntry credit;
credit.nOrderPos = IncOrderPosNext(&walletdb);
credit.strAccount = strTo;
credit.nCreditDebit = nAmount;
credit.nTime = nNow;
credit.strOtherAccount = strFrom;
credit.strComment = strComment;
AddAccountingEntry(credit, &walletdb);
return walletdb.TxnCommit();
}
bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount,
bool bForceNew) {
CWalletDB walletdb(*dbw);
CAccount account;
walletdb.ReadAccount(strAccount, account);
if (!bForceNew) {
if (!account.vchPubKey.IsValid()) {
bForceNew = true;
} else {
// Check if the current key has been used.
CScript scriptPubKey =
GetScriptForDestination(account.vchPubKey.GetID());
for (std::map<TxId, CWalletTx>::iterator it = mapWallet.begin();
it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) {
for (const CTxOut &txout : (*it).second.tx->vout) {
if (txout.scriptPubKey == scriptPubKey) {
bForceNew = true;
break;
}
}
}
}
}
// Generate a new key
if (bForceNew) {
if (!GetKeyFromPool(account.vchPubKey, false)) {
return false;
}
SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");
walletdb.WriteAccount(strAccount, account);
}
pubKey = account.vchPubKey;
return true;
}
void CWallet::MarkDirty() {
LOCK(cs_wallet);
for (std::pair<const TxId, CWalletTx> &item : mapWallet) {
item.second.MarkDirty();
}
}
bool CWallet::AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose) {
LOCK(cs_wallet);
CWalletDB walletdb(*dbw, "r+", fFlushOnClose);
const TxId &txid = wtxIn.GetId();
// Inserts only if not already there, returns tx inserted or tx found.
std::pair<std::map<TxId, CWalletTx>::iterator, bool> ret =
mapWallet.insert(std::make_pair(txid, wtxIn));
CWalletTx &wtx = (*ret.first).second;
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew) {
wtx.nTimeReceived = GetAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&walletdb);
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
wtx.nTimeSmart = ComputeTimeSmart(wtx);
AddToSpends(txid);
}
bool fUpdated = false;
if (!fInsertedNew) {
// Merge
if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) {
wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true;
}
// If no longer abandoned, update
if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) {
wtx.hashBlock = wtxIn.hashBlock;
fUpdated = true;
}
if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) {
wtx.nIndex = wtxIn.nIndex;
fUpdated = true;
}
if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) {
wtx.fFromMe = wtxIn.fFromMe;
fUpdated = true;
}
}
//// debug print
LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetId().ToString(),
(fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));
// Write to disk
if ((fInsertedNew || fUpdated) && !walletdb.WriteTx(wtx)) {
return false;
}
// Break debit/credit balance caches:
wtx.MarkDirty();
// Notify UI of new or updated transaction.
NotifyTransactionChanged(this, txid, fInsertedNew ? CT_NEW : CT_UPDATED);
// Notify an external script when a wallet transaction comes in or is
// updated.
std::string strCmd = gArgs.GetArg("-walletnotify", "");
if (!strCmd.empty()) {
boost::replace_all(strCmd, "%s", wtxIn.GetId().GetHex());
std::thread t(runCommand, strCmd);
// Thread runs free.
t.detach();
}
return true;
}
bool CWallet::LoadToWallet(const CWalletTx &wtxIn) {
const TxId &txid = wtxIn.GetId();
mapWallet[txid] = wtxIn;
CWalletTx &wtx = mapWallet[txid];
wtx.BindWallet(this);
wtxOrdered.insert(std::make_pair(wtx.nOrderPos, TxPair(&wtx, nullptr)));
AddToSpends(txid);
for (const CTxIn &txin : wtx.tx->vin) {
if (mapWallet.count(txin.prevout.GetTxId())) {
CWalletTx &prevtx = mapWallet[txin.prevout.GetTxId()];
if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {
MarkConflicted(prevtx.hashBlock, wtx.GetId());
}
}
}
return true;
}
/**
* 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 pIndex
* == nullptr, 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 CTransactionRef &ptx,
const CBlockIndex *pIndex,
int posInBlock, bool fUpdate) {
const CTransaction &tx = *ptx;
AssertLockHeld(cs_wallet);
if (pIndex != nullptr) {
for (const CTxIn &txin : tx.vin) {
std::pair<TxSpends::const_iterator, TxSpends::const_iterator>
range = mapTxSpends.equal_range(txin.prevout);
while (range.first != range.second) {
if (range.first->second != tx.GetId()) {
LogPrintf("Transaction %s (in block %s) conflicts with "
"wallet transaction %s (both spend %s:%i)\n",
tx.GetId().ToString(),
pIndex->GetBlockHash().ToString(),
range.first->second.ToString(),
range.first->first.GetTxId().ToString(),
range.first->first.GetN());
MarkConflicted(pIndex->GetBlockHash(), range.first->second);
}
range.first++;
}
}
}
bool fExisted = mapWallet.count(tx.GetId()) != 0;
if (fExisted && !fUpdate) {
return false;
}
if (fExisted || IsMine(tx) || IsFromMe(tx)) {
/**
* Check if any keys in the wallet keypool that were supposed to be
* unused have appeared in a new transaction. If so, remove those keys
* from the keypool. This can happen when restoring an old wallet backup
* that does not contain the mostly recently created transactions from
* newer versions of the wallet.
*/
// loop though all outputs
for (const CTxOut &txout : tx.vout) {
// extract addresses and check if they match with an unused keypool
// key
std::vector<CKeyID> vAffected;
CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);
for (const CKeyID &keyid : vAffected) {
std::map<CKeyID, int64_t>::const_iterator mi =
m_pool_key_to_index.find(keyid);
if (mi != m_pool_key_to_index.end()) {
LogPrintf("%s: Detected a used keypool key, mark all "
"keypool key up to this key as used\n",
__func__);
MarkReserveKeysAsUsed(mi->second);
if (!TopUpKeyPool()) {
LogPrintf(
"%s: Topping up keypool failed (locked wallet)\n",
__func__);
}
}
}
}
CWalletTx wtx(this, ptx);
// Get merkle branch if transaction was found in a block
if (pIndex != nullptr) {
wtx.SetMerkleBranch(pIndex, posInBlock);
}
return AddToWallet(wtx, false);
}
return false;
}
bool CWallet::AbandonTransaction(const TxId &txid) {
LOCK2(cs_main, cs_wallet);
CWalletDB walletdb(*dbw, "r+");
std::set<TxId> todo;
std::set<TxId> done;
// Can't mark abandoned if confirmed or in mempool.
assert(mapWallet.count(txid));
CWalletTx &origtx = mapWallet[txid];
if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool()) {
return false;
}
todo.insert(txid);
while (!todo.empty()) {
const TxId now = *todo.begin();
todo.erase(now);
done.insert(now);
assert(mapWallet.count(now));
CWalletTx &wtx = mapWallet[now];
int currentconfirm = wtx.GetDepthInMainChain();
// If the orig tx was not in block, none of its spends can be.
assert(currentconfirm <= 0);
// If (currentconfirm < 0) {Tx and spends are already conflicted, no
// need to abandon}
if (currentconfirm == 0 && !wtx.isAbandoned()) {
// If the orig tx was not in block/mempool, none of its spends can
// be in mempool.
assert(!wtx.InMempool());
wtx.nIndex = -1;
wtx.setAbandoned();
wtx.MarkDirty();
walletdb.WriteTx(wtx);
NotifyTransactionChanged(this, wtx.GetId(), CT_UPDATED);
// Iterate over all its outputs, and mark transactions in the wallet
// that spend them abandoned too.
TxSpends::const_iterator iter =
mapTxSpends.lower_bound(COutPoint(txid, 0));
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) {
if (!done.count(iter->second)) {
todo.insert(iter->second);
}
iter++;
}
// If a transaction changes 'conflicted' state, that changes the
// balance available of the outputs it spends. So force those to be
// recomputed.
for (const CTxIn &txin : wtx.tx->vin) {
if (mapWallet.count(txin.prevout.GetTxId())) {
mapWallet[txin.prevout.GetTxId()].MarkDirty();
}
}
}
}
return true;
}
void CWallet::MarkConflicted(const uint256 &hashBlock, const TxId &txid) {
LOCK2(cs_main, cs_wallet);
int conflictconfirms = 0;
if (mapBlockIndex.count(hashBlock)) {
CBlockIndex *pindex = mapBlockIndex[hashBlock];
if (chainActive.Contains(pindex)) {
conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1);
}
}
// If number of conflict confirms cannot be determined, this means that the
// block is still unknown or not yet part of the main chain, for example
// when loading the wallet during a reindex. Do nothing in that case.
if (conflictconfirms >= 0) {
return;
}
// Do not flush the wallet here for performance reasons.
CWalletDB walletdb(*dbw, "r+", false);
std::set<TxId> todo;
std::set<TxId> done;
todo.insert(txid);
while (!todo.empty()) {
const TxId now = *todo.begin();
todo.erase(now);
done.insert(now);
assert(mapWallet.count(now));
CWalletTx &wtx = mapWallet[now];
int currentconfirm = wtx.GetDepthInMainChain();
if (conflictconfirms < currentconfirm) {
// Block is 'more conflicted' than current confirm; update.
// Mark transaction as conflicted with this block.
wtx.nIndex = -1;
wtx.hashBlock = hashBlock;
wtx.MarkDirty();
walletdb.WriteTx(wtx);
// Iterate over all its outputs, and mark transactions in the wallet
// that spend them conflicted too.
TxSpends::const_iterator iter =
mapTxSpends.lower_bound(COutPoint(now, 0));
while (iter != mapTxSpends.end() && iter->first.GetTxId() == now) {
if (!done.count(iter->second)) {
todo.insert(iter->second);
}
iter++;
}
// If a transaction changes 'conflicted' state, that changes the
// balance available of the outputs it spends. So force those to be
// recomputed.
for (const CTxIn &txin : wtx.tx->vin) {
if (mapWallet.count(txin.prevout.GetTxId())) {
mapWallet[txin.prevout.GetTxId()].MarkDirty();
}
}
}
}
}
void CWallet::SyncTransaction(const CTransactionRef &ptx,
const CBlockIndex *pindex, int posInBlock) {
const CTransaction &tx = *ptx;
if (!AddToWalletIfInvolvingMe(ptx, pindex, posInBlock, true)) {
// Not one of ours
return;
}
// If a transaction changes 'conflicted' state, that changes the balance
// available of the outputs it spends. So force those to be recomputed,
// also:
for (const CTxIn &txin : tx.vin) {
if (mapWallet.count(txin.prevout.GetTxId())) {
mapWallet[txin.prevout.GetTxId()].MarkDirty();
}
}
}
void CWallet::TransactionAddedToMempool(const CTransactionRef &ptx) {
LOCK2(cs_main, cs_wallet);
SyncTransaction(ptx);
}
void CWallet::BlockConnected(
const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex,
const std::vector<CTransactionRef> &vtxConflicted) {
LOCK2(cs_main, cs_wallet);
// TODO: Tempoarily ensure that mempool removals are notified before
// connected transactions. This shouldn't matter, but the abandoned state of
// transactions in our wallet is currently cleared when we receive another
// notification and there is a race condition where notification of a
// connected conflict might cause an outside process to abandon a
// transaction and then have it inadvertantly cleared by the notification
// that the conflicted transaction was evicted.
for (const CTransactionRef &ptx : vtxConflicted) {
SyncTransaction(ptx);
}
for (size_t i = 0; i < pblock->vtx.size(); i++) {
SyncTransaction(pblock->vtx[i], pindex, i);
}
}
void CWallet::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) {
LOCK2(cs_main, cs_wallet);
for (const CTransactionRef &ptx : pblock->vtx) {
SyncTransaction(ptx);
}
}
isminetype CWallet::IsMine(const CTxIn &txin) const {
LOCK(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator mi =
mapWallet.find(txin.prevout.GetTxId());
if (mi != mapWallet.end()) {
const CWalletTx &prev = (*mi).second;
if (txin.prevout.GetN() < prev.tx->vout.size()) {
return IsMine(prev.tx->vout[txin.prevout.GetN()]);
}
}
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.
Amount CWallet::GetDebit(const CTxIn &txin, const isminefilter &filter) const {
LOCK(cs_wallet);
std::map<TxId, CWalletTx>::const_iterator mi =
mapWallet.find(txin.prevout.GetTxId());
if (mi != mapWallet.end()) {
const CWalletTx &prev = (*mi).second;
if (txin.prevout.GetN() < prev.tx->vout.size()) {
if (IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter) {
return prev.tx->vout[txin.prevout.GetN()].nValue;
}
}
}
return Amount::zero();
}
isminetype CWallet::IsMine(const CTxOut &txout) const {
return ::IsMine(*this, txout.scriptPubKey);
}
Amount CWallet::GetCredit(const CTxOut &txout,
const isminefilter &filter) const {
if (!MoneyRange(txout.nValue)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
return (IsMine(txout) & filter) ? txout.nValue : Amount::zero();
}
bool CWallet::IsChange(const CTxOut &txout) const {
// TODO: fix handling of 'change' outputs. The assumption is that any
// payment to a script that is ours, but is not in the address book is
// change. That assumption is likely to break when we implement
// multisignature wallets that return change back into a
// multi-signature-protected address; a better way of identifying which
// outputs are 'the send' and which are 'the change' will need to be
// implemented (maybe extend CWalletTx to remember which output, if any, was
// change).
if (::IsMine(*this, txout.scriptPubKey)) {
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address)) {
return true;
}
LOCK(cs_wallet);
if (!mapAddressBook.count(address)) {
return true;
}
}
return false;
}
Amount CWallet::GetChange(const CTxOut &txout) const {
if (!MoneyRange(txout.nValue)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
return (IsChange(txout) ? txout.nValue : Amount::zero());
}
bool CWallet::IsMine(const CTransaction &tx) const {
for (const CTxOut &txout : tx.vout) {
if (IsMine(txout)) {
return true;
}
}
return false;
}
bool CWallet::IsFromMe(const CTransaction &tx) const {
return GetDebit(tx, ISMINE_ALL) > Amount::zero();
}
Amount CWallet::GetDebit(const CTransaction &tx,
const isminefilter &filter) const {
Amount nDebit = Amount::zero();
for (const CTxIn &txin : tx.vin) {
nDebit += GetDebit(txin, filter);
if (!MoneyRange(nDebit)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
return nDebit;
}
bool CWallet::IsAllFromMe(const CTransaction &tx,
const isminefilter &filter) const {
LOCK(cs_wallet);
for (const CTxIn &txin : tx.vin) {
auto mi = mapWallet.find(txin.prevout.GetTxId());
if (mi == mapWallet.end()) {
// Any unknown inputs can't be from us.
return false;
}
const CWalletTx &prev = (*mi).second;
if (txin.prevout.GetN() >= prev.tx->vout.size()) {
// Invalid input!
return false;
}
if (!(IsMine(prev.tx->vout[txin.prevout.GetN()]) & filter)) {
return false;
}
}
return true;
}
Amount CWallet::GetCredit(const CTransaction &tx,
const isminefilter &filter) const {
Amount nCredit = Amount::zero();
for (const CTxOut &txout : tx.vout) {
nCredit += GetCredit(txout, filter);
if (!MoneyRange(nCredit)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
return nCredit;
}
Amount CWallet::GetChange(const CTransaction &tx) const {
Amount nChange = Amount::zero();
for (const CTxOut &txout : tx.vout) {
nChange += GetChange(txout);
if (!MoneyRange(nChange)) {
throw std::runtime_error(std::string(__func__) +
": value out of range");
}
}
return nChange;
}
CPubKey CWallet::GenerateNewHDMasterKey() {
CKey key;
key.MakeNewKey(true);
int64_t nCreationTime = GetTime();
CKeyMetadata metadata(nCreationTime);
// Calculate the pubkey.
CPubKey pubkey = key.GetPubKey();
assert(key.VerifyPubKey(pubkey));
// Set the hd keypath to "m" -> Master, refers the masterkeyid to itself.
metadata.hdKeypath = "m";
metadata.hdMasterKeyID = pubkey.GetID();
LOCK(cs_wallet);
// mem store the metadata
mapKeyMetadata[pubkey.GetID()] = metadata;
// Write the key&metadata to the database.
if (!AddKeyPubKey(key, pubkey)) {
throw std::runtime_error(std::string(__func__) +
": AddKeyPubKey failed");
}
return pubkey;
}
bool CWallet::SetHDMasterKey(const CPubKey &pubkey,
CHDChain *possibleOldChain) {
LOCK(cs_wallet);
// Store the keyid (hash160) together with the child index counter in the
// database as a hdchain object.
CHDChain newHdChain;
if (possibleOldChain) {
// preserve the old chains version
newHdChain.nVersion = possibleOldChain->nVersion;
}
newHdChain.masterKeyID = pubkey.GetID();
SetHDChain(newHdChain, false);
return true;
}
bool CWallet::SetHDChain(const CHDChain &chain, bool memonly) {
LOCK(cs_wallet);
if (!memonly && !CWalletDB(*dbw).WriteHDChain(chain)) {
throw std::runtime_error(std::string(__func__) +
": writing chain failed");
}
hdChain = chain;
return true;
}
bool CWallet::IsHDEnabled() {
return !hdChain.masterKeyID.IsNull();
}
int64_t CWalletTx::GetTxTime() const {
int64_t n = nTimeSmart;
return n ? n : nTimeReceived;
}
int CWalletTx::GetRequestCount() const {
LOCK(pwallet->cs_wallet);
// Returns -1 if it wasn't being tracked.
int nRequests = -1;
if (IsCoinBase()) {
// Generated block.
if (!hashUnset()) {
std::map<uint256, int>::const_iterator mi =
pwallet->mapRequestCount.find(hashBlock);
if (mi != pwallet->mapRequestCount.end()) {
nRequests = (*mi).second;
}
}
} else {
// Did anyone request this transaction?
std::map<uint256, int>::const_iterator mi =
pwallet->mapRequestCount.find(GetId());
if (mi != pwallet->mapRequestCount.end()) {
nRequests = (*mi).second;
// How about the block it's in?
if (nRequests == 0 && !hashUnset()) {
std::map<uint256, int>::const_iterator _mi =
pwallet->mapRequestCount.find(hashBlock);
if (_mi != pwallet->mapRequestCount.end()) {
nRequests = (*_mi).second;
} else {
// If it's in someone else's block it must have got out.
nRequests = 1;
}
}
}
}
return nRequests;
}
void CWalletTx::GetAmounts(std::list<COutputEntry> &listReceived,
std::list<COutputEntry> &listSent, Amount &nFee,
std::string &strSentAccount,
const isminefilter &filter) const {
nFee = Amount::zero();
listReceived.clear();
listSent.clear();
strSentAccount = strFromAccount;
// Compute fee:
Amount nDebit = GetDebit(filter);
// debit>0 means we signed/sent this transaction.
if (nDebit > Amount::zero()) {
Amount nValueOut = tx->GetValueOut();
nFee = (nDebit - nValueOut);
}
// Sent/received.
for (unsigned int i = 0; i < tx->vout.size(); ++i) {
const CTxOut &txout = tx->vout[i];
isminetype fIsMine = pwallet->IsMine(txout);
// Only need to handle txouts if AT LEAST one of these is true:
// 1) they debit from us (sent)
// 2) the output is to us (received)
if (nDebit > Amount::zero()) {
// Don't report 'change' txouts
if (pwallet->IsChange(txout)) {
continue;
}
} else if (!(fIsMine & filter)) {
continue;
}
// In either case, we need to get the destination address.
CTxDestination address;
if (!ExtractDestination(txout.scriptPubKey, address) &&
!txout.scriptPubKey.IsUnspendable()) {
LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, "
"txid %s\n",
this->GetId().ToString());
address = CNoDestination();
}
COutputEntry output = {address, txout.nValue, (int)i};
// If we are debited by the transaction, add the output as a "sent"
// entry.
if (nDebit > Amount::zero()) {
listSent.push_back(output);
}
// If we are receiving the output, add it as a "received" entry.
if (fIsMine & filter) {
listReceived.push_back(output);
}
}
}
/**
* Scan active chain for relevant transactions after importing keys. This should
* be called whenever new keys are added to the wallet, with the oldest key
* creation time.
*
* @return Earliest timestamp that could be successfully scanned from. Timestamp
* returned will be higher than startTime if relevant blocks could not be read.
*/
int64_t CWallet::RescanFromTime(int64_t startTime, bool update) {
AssertLockHeld(cs_main);
AssertLockHeld(cs_wallet);
// Find starting block. May be null if nCreateTime is greater than the
// highest blockchain timestamp, in which case there is nothing that needs
// to be scanned.
CBlockIndex *const startBlock =
chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW);
LogPrintf("%s: Rescanning last %i blocks\n", __func__,
startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
if (startBlock) {
const CBlockIndex *const failedBlock =
ScanForWalletTransactions(startBlock, nullptr, update);
if (failedBlock) {
return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
}
}
return startTime;
}
/**
* Scan the block chain (starting in pindexStart) for transactions from or to
* us. If fUpdate is true, found transactions that already exist in the wallet
* will be updated.
*
* Returns null if scan was successful. Otherwise, if a complete rescan was not
* possible (due to pruning or corruption), returns pointer to the most recent
* block that could not be scanned.
*
* If pindexStop is not a nullptr, the scan will stop at the block-index defined
* by pindexStop.
*/
CBlockIndex *CWallet::ScanForWalletTransactions(CBlockIndex *pindexStart,
CBlockIndex *pindexStop,
bool fUpdate) {
int64_t nNow = GetTime();
if (pindexStop) {
assert(pindexStop->nHeight >= pindexStart->nHeight);
}
CBlockIndex *pindex = pindexStart;
CBlockIndex *ret = nullptr;
LOCK2(cs_main, cs_wallet);
fAbortRescan = false;
fScanningWallet = true;
// Show rescan progress in GUI as dialog or on splashscreen, if -rescan on
// startup.
ShowProgress(_("Rescanning..."), 0);
double dProgressStart =
GuessVerificationProgress(chainParams.TxData(), pindex);
double dProgressTip =
GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());
while (pindex && !fAbortRescan) {
if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) {
ShowProgress(
_("Rescanning..."),
std::max(1, std::min<int>(99,
(GuessVerificationProgress(
chainParams.TxData(), pindex) -
dProgressStart) /
(dProgressTip - dProgressStart) *
100)));
}
CBlock block;
if (ReadBlockFromDisk(block, pindex, GetConfig())) {
for (size_t posInBlock = 0; posInBlock < block.vtx.size();
++posInBlock) {
AddToWalletIfInvolvingMe(block.vtx[posInBlock], pindex,
posInBlock, fUpdate);
}
} else {
ret = pindex;
}
if (pindex == pindexStop) {
break;
}
pindex = chainActive.Next(pindex);
if (GetTime() >= nNow + 60) {
nNow = GetTime();
LogPrintf("Still rescanning. At block %d. Progress=%f\n",
pindex->nHeight,
GuessVerificationProgress(chainParams.TxData(), pindex));
}
}
if (pindex && fAbortRescan) {
LogPrintf("Rescan aborted at block %d. Progress=%f\n", pindex->nHeight,
GuessVerificationProgress(chainParams.TxData(), pindex));
}
// Hide progress dialog in GUI.
ShowProgress(_("Rescanning..."), 100);
fScanningWallet = false;
return ret;
}
void CWallet::ReacceptWalletTransactions() {
// If transactions aren't being broadcasted, don't let them into local
// mempool either.
if (!fBroadcastTransactions) {
return;
}
LOCK2(cs_main, cs_wallet);
std::map<int64_t, CWalletTx *> mapSorted;
// Sort pending wallet transactions based on their initial wallet insertion
// order.
for (std::pair<const TxId, CWalletTx> &item : mapWallet) {
const TxId &wtxid = item.first;
CWalletTx &wtx = item.second;
assert(wtx.GetId() == wtxid);
int nDepth = wtx.GetDepthInMainChain();
if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned())) {
mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));
}
}
// Try to add wallet transactions to memory pool.
for (std::pair<const int64_t, CWalletTx *> &item : mapSorted) {
CWalletTx &wtx = *(item.second);
LOCK(mempool.cs);
CValidationState state;
wtx.AcceptToMemoryPool(maxTxFee, state);
}
}
bool CWalletTx::RelayWalletTransaction(CConnman *connman) {
assert(pwallet->GetBroadcastTransactions());
if (IsCoinBase() || isAbandoned() || GetDepthInMainChain() != 0) {
return false;
}
CValidationState state;
// GetDepthInMainChain already catches known conflicts.
if (InMempool() || AcceptToMemoryPool(maxTxFee, state)) {
LogPrintf("Relaying wtx %s\n", GetId().ToString());
if (connman) {
CInv inv(MSG_TX, GetId());
connman->ForEachNode(
[&inv](CNode *pnode) { pnode->PushInventory(inv); });
return true;
}
}
return false;
}
std::set<TxId> CWalletTx::GetConflicts() const {
std::set<TxId> result;
if (pwallet != nullptr) {
const TxId &txid = GetId();
result = pwallet->GetConflicts(txid);
result.erase(txid);
}
return result;
}
Amount CWalletTx::GetDebit(const isminefilter &filter) const {
if (tx->vin.empty()) {
return Amount::zero();
}
Amount debit = Amount::zero();
if (filter & ISMINE_SPENDABLE) {
if (fDebitCached) {
debit += nDebitCached;
} else {
nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);
fDebitCached = true;
debit += nDebitCached;
}
}
if (filter & ISMINE_WATCH_ONLY) {
if (fWatchDebitCached) {
debit += nWatchDebitCached;
} else {
nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);
fWatchDebitCached = true;
debit += Amount(nWatchDebitCached);
}
}
return debit;
}
Amount CWalletTx::GetCredit(const isminefilter &filter) const {
// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
- if (IsCoinBase() && GetBlocksToMaturity() > 0) {
+ if (IsImmatureCoinBase()) {
return Amount::zero();
}
Amount credit = Amount::zero();
if (filter & ISMINE_SPENDABLE) {
// GetBalance can assume transactions in mapWallet won't change.
if (fCreditCached) {
credit += nCreditCached;
} else {
nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fCreditCached = true;
credit += nCreditCached;
}
}
if (filter & ISMINE_WATCH_ONLY) {
if (fWatchCreditCached) {
credit += nWatchCreditCached;
} else {
nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
fWatchCreditCached = true;
credit += nWatchCreditCached;
}
}
return credit;
}
Amount CWalletTx::GetImmatureCredit(bool fUseCache) const {
- if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) {
+ if (IsImmatureCoinBase() && IsInMainChain()) {
if (fUseCache && fImmatureCreditCached) {
return nImmatureCreditCached;
}
nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);
fImmatureCreditCached = true;
return nImmatureCreditCached;
}
return Amount::zero();
}
Amount CWalletTx::GetAvailableCredit(bool fUseCache) const {
if (pwallet == 0) {
return Amount::zero();
}
// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
- if (IsCoinBase() && GetBlocksToMaturity() > 0) {
+ if (IsImmatureCoinBase()) {
return Amount::zero();
}
if (fUseCache && fAvailableCreditCached) {
return nAvailableCreditCached;
}
Amount nCredit = Amount::zero();
for (uint32_t i = 0; i < tx->vout.size(); i++) {
if (!pwallet->IsSpent(GetId(), i)) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_SPENDABLE);
if (!MoneyRange(nCredit)) {
throw std::runtime_error(
"CWalletTx::GetAvailableCredit() : value out of range");
}
}
}
nAvailableCreditCached = nCredit;
fAvailableCreditCached = true;
return nCredit;
}
Amount CWalletTx::GetImmatureWatchOnlyCredit(const bool &fUseCache) const {
- if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) {
+ if (IsImmatureCoinBase() && IsInMainChain()) {
if (fUseCache && fImmatureWatchCreditCached) {
return nImmatureWatchCreditCached;
}
nImmatureWatchCreditCached =
pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);
fImmatureWatchCreditCached = true;
return nImmatureWatchCreditCached;
}
return Amount::zero();
}
Amount CWalletTx::GetAvailableWatchOnlyCredit(const bool &fUseCache) const {
if (pwallet == 0) {
return Amount::zero();
}
// Must wait until coinbase is safely deep enough in the chain before
// valuing it.
if (IsCoinBase() && GetBlocksToMaturity() > 0) {
return Amount::zero();
}
if (fUseCache && fAvailableWatchCreditCached) {
return nAvailableWatchCreditCached;
}
Amount nCredit = Amount::zero();
for (uint32_t i = 0; i < tx->vout.size(); i++) {
if (!pwallet->IsSpent(GetId(), i)) {
const CTxOut &txout = tx->vout[i];
nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);
if (!MoneyRange(nCredit)) {
throw std::runtime_error(
"CWalletTx::GetAvailableCredit() : value out of range");
}
}
}
nAvailableWatchCreditCached = nCredit;
fAvailableWatchCreditCached = true;
return nCredit;
}
Amount CWalletTx::GetChange() const {
if (fChangeCached) {
return nChangeCached;
}
nChangeCached = pwallet->GetChange(*this);
fChangeCached = true;
return nChangeCached;
}
bool CWalletTx::InMempool() const {
LOCK(mempool.cs);
if (mempool.exists(GetId())) {
return true;
}
return false;
}
bool CWalletTx::IsTrusted() const {
// Quick answer in most cases
if (!CheckFinalTx(*this)) {
return false;
}
int nDepth = GetDepthInMainChain();
if (nDepth >= 1) {
return true;
}
if (nDepth < 0) {
return false;
}
// using wtx's cached debit
if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) {
return false;
}
// Don't trust unconfirmed transactions from us unless they are in the
// mempool.
if (!InMempool()) {
return false;
}
// Trusted if all inputs are from us and are in the mempool:
for (const CTxIn &txin : tx->vin) {
// Transactions not sent by us: not trusted
const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.GetTxId());
if (parent == nullptr) {
return false;
}
const CTxOut &parentOut = parent->tx->vout[txin.prevout.GetN()];
if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) {
return false;
}
}
return true;
}
bool CWalletTx::IsEquivalentTo(const CWalletTx &_tx) const {
CMutableTransaction tx1 = *this->tx;
CMutableTransaction tx2 = *_tx.tx;
for (CTxIn &in : tx1.vin) {
in.scriptSig = CScript();
}
for (CTxIn &in : tx2.vin) {
in.scriptSig = CScript();
}
return CTransaction(tx1) == CTransaction(tx2);
}
std::vector<uint256>
CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman *connman) {
std::vector<uint256> result;
LOCK(cs_wallet);
// Sort them in chronological order
std::multimap<unsigned int, CWalletTx *> mapSorted;
for (std::pair<const TxId, CWalletTx> &item : mapWallet) {
CWalletTx &wtx = item.second;
// Don't rebroadcast if newer than nTime:
if (wtx.nTimeReceived > nTime) {
continue;
}
mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx));
}
for (std::pair<const unsigned int, CWalletTx *> &item : mapSorted) {
CWalletTx &wtx = *item.second;
if (wtx.RelayWalletTransaction(connman)) {
result.push_back(wtx.GetId());
}
}
return result;
}
void CWallet::ResendWalletTransactions(int64_t nBestBlockTime,
CConnman *connman) {
// Do this infrequently and randomly to avoid giving away that these are our
// transactions.
if (GetTime() < nNextResend || !fBroadcastTransactions) {
return;
}
bool fFirst = (nNextResend == 0);
nNextResend = GetTime() + GetRand(30 * 60);
if (fFirst) {
return;
}
// Only do it if there's been a new block since last time
if (nBestBlockTime < nLastResend) {
return;
}
nLastResend = GetTime();
// Rebroadcast unconfirmed txes older than 5 minutes before the last block
// was found:
std::vector<uint256> relayed =
ResendWalletTransactionsBefore(nBestBlockTime - 5 * 60, connman);
if (!relayed.empty()) {
LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__,
relayed.size());
}
}
/** @} */ // end of mapWallet
/**
* @defgroup Actions
*
* @{
*/
Amount CWallet::GetBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const std::pair<uint256, CWalletTx> &p : mapWallet) {
const CWalletTx *pcoin = &p.second;
if (pcoin->IsTrusted()) {
nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
Amount CWallet::GetUnconfirmedBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const std::pair<uint256, CWalletTx> &p : mapWallet) {
const CWalletTx *pcoin = &p.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 &&
pcoin->InMempool()) {
nTotal += pcoin->GetAvailableCredit();
}
}
return nTotal;
}
Amount CWallet::GetImmatureBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const std::pair<uint256, CWalletTx> &p : mapWallet) {
const CWalletTx *pcoin = &p.second;
nTotal += pcoin->GetImmatureCredit();
}
return nTotal;
}
Amount CWallet::GetWatchOnlyBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const std::pair<uint256, CWalletTx> &p : mapWallet) {
const CWalletTx *pcoin = &p.second;
if (pcoin->IsTrusted()) {
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
Amount CWallet::GetUnconfirmedWatchOnlyBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const std::pair<uint256, CWalletTx> &p : mapWallet) {
const CWalletTx *pcoin = &p.second;
if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 &&
pcoin->InMempool()) {
nTotal += pcoin->GetAvailableWatchOnlyCredit();
}
}
return nTotal;
}
Amount CWallet::GetImmatureWatchOnlyBalance() const {
LOCK2(cs_main, cs_wallet);
Amount nTotal = Amount::zero();
for (const std::pair<uint256, CWalletTx> &p : mapWallet) {
const CWalletTx *pcoin = &p.second;
nTotal += pcoin->GetImmatureWatchOnlyCredit();
}
return nTotal;
}
// Calculate total balance in a 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.
Amount CWallet::GetLegacyBalance(const isminefilter &filter, int minDepth,
const std::string *account) const {
LOCK2(cs_main, cs_wallet);
Amount balance = Amount::zero();
for (const auto &entry : mapWallet) {
const CWalletTx &wtx = entry.second;
const int depth = wtx.GetDepthInMainChain();
- if (depth < 0 || !CheckFinalTx(*wtx.tx) ||
- wtx.GetBlocksToMaturity() > 0) {
+ if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase()) {
+
continue;
}
// Loop through tx outputs and add incoming payments. For outgoing txs,
// treat change outputs specially, as part of the amount debited.
Amount debit = wtx.GetDebit(filter);
const bool outgoing = debit > Amount::zero();
for (const CTxOut &out : wtx.tx->vout) {
if (outgoing && IsChange(out)) {
debit -= out.nValue;
} else if (IsMine(out) & filter && depth >= minDepth &&
(!account ||
*account == GetAccountName(out.scriptPubKey))) {
balance += out.nValue;
}
}
// For outgoing txs, subtract amount debited.
if (outgoing && (!account || *account == wtx.strFromAccount)) {
balance -= debit;
}
}
if (account) {
balance += CWalletDB(*dbw).GetAccountCreditDebit(*account);
}
return balance;
}
void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe,
const CCoinControl *coinControl,
const Amount nMinimumAmount,
const Amount nMaximumAmount,
const Amount nMinimumSumAmount,
const uint64_t &nMaximumCount,
const int &nMinDepth, const int &nMaxDepth) const {
vCoins.clear();
Amount nTotal = Amount::zero();
LOCK2(cs_main, cs_wallet);
for (std::map<TxId, CWalletTx>::const_iterator it = mapWallet.begin();
it != mapWallet.end(); ++it) {
const TxId &wtxid = it->first;
const CWalletTx *pcoin = &(*it).second;
if (!CheckFinalTx(*pcoin)) {
continue;
}
- if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) {
+ if (pcoin->IsImmatureCoinBase()) {
continue;
}
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < 0) {
continue;
}
// We should not consider coins which aren't at least in our mempool.
// It's possible for these to be conflicted via ancestors which we may
// never be able to detect.
if (nDepth == 0 && !pcoin->InMempool()) {
continue;
}
bool safeTx = pcoin->IsTrusted();
// Bitcoin-ABC: Removed check that prevents consideration of coins from
// transactions that are replacing other transactions. This check based
// on pcoin->mapValue.count("replaces_txid") which was not being set
// anywhere.
// 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).
// Bitcoin-ABC: retained this check as 'replaced_by_txid' is still set
// in the wallet code.
if (nDepth == 0 && pcoin->mapValue.count("replaced_by_txid")) {
safeTx = false;
}
if (fOnlySafe && !safeTx) {
continue;
}
if (nDepth < nMinDepth || nDepth > nMaxDepth) {
continue;
}
for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) {
if (pcoin->tx->vout[i].nValue < nMinimumAmount ||
pcoin->tx->vout[i].nValue > nMaximumAmount) {
continue;
}
if (coinControl && coinControl->HasSelected() &&
!coinControl->fAllowOtherInputs &&
!coinControl->IsSelected(COutPoint((*it).first, i))) {
continue;
}
if (IsLockedCoin((*it).first, i)) {
continue;
}
if (IsSpent(wtxid, i)) {
continue;
}
isminetype mine = IsMine(pcoin->tx->vout[i]);
if (mine == ISMINE_NO) {
continue;
}
bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||
(coinControl && coinControl->fAllowWatchOnly &&
(mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO);
bool fSolvableIn =
(mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) !=
ISMINE_NO;
vCoins.push_back(
COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx));
// Checks the sum amount of all UTXO's.
if (nMinimumSumAmount != MAX_MONEY) {
nTotal += pcoin->tx->vout[i].nValue;
if (nTotal >= nMinimumSumAmount) {
return;
}
}
// Checks the maximum number of UTXO's.
if (nMaximumCount > 0 && vCoins.size() >= nMaximumCount) {
return;
}
}
}
}
static void ApproximateBestSubset(
std::vector<std::pair<Amount, std::pair<const CWalletTx *, unsigned int>>>
vValue,
const Amount nTotalLower, const Amount nTargetValue,
std::vector<char> &vfBest, Amount &nBest, int iterations = 1000) {
std::vector<char> vfIncluded;
vfBest.assign(vValue.size(), true);
nBest = nTotalLower;
FastRandomContext insecure_rand;
for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) {
vfIncluded.assign(vValue.size(), false);
Amount nTotal = Amount::zero();
bool fReachedTarget = false;
for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++) {
for (size_t i = 0; i < vValue.size(); i++) {
// The solver here uses a randomized algorithm, the randomness
// serves no real security purpose but is just needed to prevent
// degenerate behavior and it is important that the rng is fast.
// We do not use a constant random sequence, because there may
// be some privacy improvement by making the selection random.
if (nPass == 0 ? insecure_rand.randbool() : !vfIncluded[i]) {
nTotal += vValue[i].first;
vfIncluded[i] = true;
if (nTotal >= nTargetValue) {
fReachedTarget = true;
if (nTotal < nBest) {
nBest = nTotal;
vfBest = vfIncluded;
}
nTotal -= vValue[i].first;
vfIncluded[i] = false;
}
}
}
}
}
}
bool CWallet::SelectCoinsMinConf(
const Amount nTargetValue, const int nConfMine, const int nConfTheirs,
const uint64_t nMaxAncestors, std::vector<COutput> vCoins,
std::set<std::pair<const CWalletTx *, unsigned int>> &setCoinsRet,
Amount &nValueRet) const {
setCoinsRet.clear();
nValueRet = Amount::zero();
// List of values less than target
std::pair<Amount, std::pair<const CWalletTx *, unsigned int>>
coinLowestLarger;
coinLowestLarger.first = MAX_MONEY;
coinLowestLarger.second.first = nullptr;
std::vector<std::pair<Amount, std::pair<const CWalletTx *, unsigned int>>>
vValue;
Amount nTotalLower = Amount::zero();
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
for (const COutput &output : vCoins) {
if (!output.fSpendable) {
continue;
}
const CWalletTx *pcoin = output.tx;
if (output.nDepth <
(pcoin->IsFromMe(ISMINE_ALL) ? nConfMine : nConfTheirs)) {
continue;
}
if (!mempool.TransactionWithinChainLimit(pcoin->GetId(),
nMaxAncestors)) {
continue;
}
int i = output.i;
Amount n = pcoin->tx->vout[i].nValue;
std::pair<Amount, std::pair<const CWalletTx *, unsigned int>> coin =
std::make_pair(n, std::make_pair(pcoin, i));
if (n == nTargetValue) {
setCoinsRet.insert(coin.second);
nValueRet += coin.first;
return true;
}
if (n < nTargetValue + MIN_CHANGE) {
vValue.push_back(coin);
nTotalLower += n;
} else if (n < coinLowestLarger.first) {
coinLowestLarger = coin;
}
}
if (nTotalLower == nTargetValue) {
for (unsigned int i = 0; i < vValue.size(); ++i) {
setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
}
return true;
}
if (nTotalLower < nTargetValue) {
if (coinLowestLarger.second.first == nullptr) {
return false;
}
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
return true;
}
// Solve subset sum by stochastic approximation
std::sort(vValue.begin(), vValue.end(), CompareValueOnly());
std::reverse(vValue.begin(), vValue.end());
std::vector<char> vfBest;
Amount nBest;
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest);
if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE) {
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE,
vfBest, nBest);
}
// If we have a bigger coin and (either the stochastic approximation didn't
// find a good solution, or the next bigger coin is closer), return the
// bigger coin.
if (coinLowestLarger.second.first &&
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) ||
coinLowestLarger.first <= nBest)) {
setCoinsRet.insert(coinLowestLarger.second);
nValueRet += coinLowestLarger.first;
} else {
for (unsigned int i = 0; i < vValue.size(); i++) {
if (vfBest[i]) {
setCoinsRet.insert(vValue[i].second);
nValueRet += vValue[i].first;
}
}
if (LogAcceptCategory(BCLog::SELECTCOINS)) {
LogPrint(BCLog::SELECTCOINS, "SelectCoins() best subset: ");
for (size_t i = 0; i < vValue.size(); i++) {
if (vfBest[i]) {
LogPrint(BCLog::SELECTCOINS, "%s ",
FormatMoney(vValue[i].first));
}
}
LogPrint(BCLog::SELECTCOINS, "total %s\n", FormatMoney(nBest));
}
}
return true;
}
bool CWallet::SelectCoins(
const std::vector<COutput> &vAvailableCoins, const Amount nTargetValue,
std::set<std::pair<const CWalletTx *, unsigned int>> &setCoinsRet,
Amount &nValueRet, const CCoinControl *coinControl) const {
std::vector<COutput> vCoins(vAvailableCoins);
// coin control -> return all selected outputs (we want all selected to go
// into the transaction for sure).
if (coinControl && coinControl->HasSelected() &&
!coinControl->fAllowOtherInputs) {
for (const COutput &out : vCoins) {
if (!out.fSpendable) {
continue;
}
nValueRet += out.tx->tx->vout[out.i].nValue;
setCoinsRet.insert(std::make_pair(out.tx, out.i));
}
return (nValueRet >= nTargetValue);
}
// Calculate value from preset inputs and store them.
std::set<std::pair<const CWalletTx *, uint32_t>> setPresetCoins;
Amount nValueFromPresetInputs = Amount::zero();
std::vector<COutPoint> vPresetInputs;
if (coinControl) {
coinControl->ListSelected(vPresetInputs);
}
for (const COutPoint &outpoint : vPresetInputs) {
std::map<TxId, CWalletTx>::const_iterator it =
mapWallet.find(outpoint.GetTxId());
if (it == mapWallet.end()) {
// TODO: Allow non-wallet inputs
return false;
}
const CWalletTx *pcoin = &it->second;
// Clearly invalid input, fail.
if (pcoin->tx->vout.size() <= outpoint.GetN()) {
return false;
}
nValueFromPresetInputs += pcoin->tx->vout[outpoint.GetN()].nValue;
setPresetCoins.insert(std::make_pair(pcoin, outpoint.GetN()));
}
// Remove preset inputs from vCoins.
for (std::vector<COutput>::iterator it = vCoins.begin();
it != vCoins.end() && coinControl && coinControl->HasSelected();) {
if (setPresetCoins.count(std::make_pair(it->tx, it->i))) {
it = vCoins.erase(it);
} else {
++it;
}
}
size_t nMaxChainLength = std::min(
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT),
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));
bool fRejectLongChains = gArgs.GetBoolArg(
"-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
bool res =
nTargetValue <= nValueFromPresetInputs ||
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());
// Add preset inputs to the total value selected.
nValueRet += nValueFromPresetInputs;
return res;
}
bool CWallet::FundTransaction(CMutableTransaction &tx, Amount &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) {
std::vector<CRecipient> vecSend;
// Turn the txout set into a CRecipient vector.
for (size_t idx = 0; idx < tx.vout.size(); idx++) {
const CTxOut &txOut = tx.vout[idx];
CRecipient recipient = {txOut.scriptPubKey, txOut.nValue,
setSubtractFeeFromOutputs.count(idx) == 1};
vecSend.push_back(recipient);
}
CCoinControl coinControl;
coinControl.destChange = destChange;
coinControl.fAllowOtherInputs = true;
coinControl.fAllowWatchOnly = includeWatching;
coinControl.fOverrideFeeRate = overrideEstimatedFeeRate;
coinControl.nFeeRate = specificFeeRate;
for (const CTxIn &txin : tx.vin) {
coinControl.Select(txin.prevout);
}
CReserveKey reservekey(this);
CWalletTx wtx;
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut,
strFailReason, &coinControl, false)) {
return false;
}
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 (size_t idx = 0; idx < tx.vout.size(); idx++) {
tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;
}
// Add new txins (keeping original txin scriptSig/order)
for (const CTxIn &txin : wtx.tx->vin) {
if (!coinControl.IsSelected(txin.prevout)) {
tx.vin.push_back(txin);
if (lockUnspents) {
LOCK2(cs_main, cs_wallet);
LockCoin(txin.prevout);
}
}
}
// Optionally keep the change output key.
if (keepReserveKey) {
reservekey.KeepKey();
}
return true;
}
bool CWallet::CreateTransaction(const std::vector<CRecipient> &vecSend,
CWalletTx &wtxNew, CReserveKey &reservekey,
Amount &nFeeRet, int &nChangePosInOut,
std::string &strFailReason,
const CCoinControl *coinControl, bool sign) {
Amount nValue = Amount::zero();
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
for (const auto &recipient : vecSend) {
if (nValue < Amount::zero() || recipient.nAmount < Amount::zero()) {
strFailReason = _("Transaction amounts must not be negative");
return false;
}
nValue += recipient.nAmount;
if (recipient.fSubtractFeeFromAmount) {
nSubtractFeeFromAmount++;
}
}
if (vecSend.empty()) {
strFailReason = _("Transaction must have at least one recipient");
return false;
}
wtxNew.fTimeReceivedIsTxTime = true;
wtxNew.BindWallet(this);
CMutableTransaction txNew;
// Discourage fee sniping.
//
// For a large miner the value of the transactions in the best block and the
// mempool can exceed the cost of deliberately attempting to mine two blocks
// to orphan the current best block. By setting nLockTime such that only the
// next block can include the transaction, we discourage this practice as
// the height restricted and limited blocksize gives miners considering fee
// sniping fewer options for pulling off this attack.
//
// A simple way to think about this is from the wallet's point of view we
// always want the blockchain to move forward. By setting nLockTime this way
// we're basically making the statement that we only want this transaction
// to appear in the next block; we don't want to potentially encourage
// reorgs by allowing transactions to appear at lower heights than the next
// block in forks of the best chain.
//
// Of course, the subsidy is high enough, and transaction volume low enough,
// that fee sniping isn't a problem yet, but by implementing a fix now we
// ensure code won't be written that makes assumptions about nLockTime that
// preclude a fix later.
txNew.nLockTime = chainActive.Height();
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
// e.g. high-latency mix networks and some CoinJoin implementations, have
// better privacy.
if (GetRandInt(10) == 0) {
txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));
}
assert(txNew.nLockTime <= (unsigned int)chainActive.Height());
assert(txNew.nLockTime < LOCKTIME_THRESHOLD);
{
std::set<std::pair<const CWalletTx *, uint32_t>> setCoins;
LOCK2(cs_main, cs_wallet);
std::vector<COutput> vAvailableCoins;
AvailableCoins(vAvailableCoins, true, coinControl);
nFeeRet = Amount::zero();
// Start with no fee and loop until there is enough fee.
while (true) {
nChangePosInOut = nChangePosRequest;
txNew.vin.clear();
txNew.vout.clear();
wtxNew.fFromMe = true;
bool fFirst = true;
Amount nValueToSelect = nValue;
if (nSubtractFeeFromAmount == 0) {
nValueToSelect += nFeeRet;
}
double dPriority = 0;
// vouts to the payees
for (const auto &recipient : vecSend) {
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
if (recipient.fSubtractFeeFromAmount) {
// Subtract fee equally from each selected recipient.
txout.nValue -= nFeeRet / int(nSubtractFeeFromAmount);
// First receiver pays the remainder not divisible by output
// count.
if (fFirst) {
fFirst = false;
txout.nValue -= nFeeRet % int(nSubtractFeeFromAmount);
}
}
if (txout.IsDust(dustRelayFee)) {
if (recipient.fSubtractFeeFromAmount &&
nFeeRet > Amount::zero()) {
if (txout.nValue < Amount::zero()) {
strFailReason = _("The transaction amount is "
"too small to pay the fee");
} else {
strFailReason =
_("The transaction amount is too small to "
"send after the fee has been deducted");
}
} else {
strFailReason = _("Transaction amount too small");
}
return false;
}
txNew.vout.push_back(txout);
}
// Choose coins to use.
Amount nValueIn = Amount::zero();
setCoins.clear();
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins,
nValueIn, coinControl)) {
strFailReason = _("Insufficient funds");
return false;
}
for (const auto &pcoin : setCoins) {
Amount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;
// The coin age after the next block (depth+1) is used instead
// of the current, reflecting an assumption the user would
// accept a bit more delay for a chance at a free transaction.
// But mempool inputs might still be in the mempool, so their
// age stays 0.
int age = pcoin.first->GetDepthInMainChain();
assert(age >= 0);
if (age != 0) {
age += 1;
}
dPriority += (age * nCredit) / SATOSHI;
}
const Amount nChange = nValueIn - nValueToSelect;
if (nChange > Amount::zero()) {
// Fill a vout to ourself.
// TODO: pass in scriptChange instead of reservekey so change
// transaction isn't always pay-to-bitcoin-address.
CScript scriptChange;
// Coin control: send change to custom address.
if (coinControl &&
!boost::get<CNoDestination>(&coinControl->destChange)) {
scriptChange =
GetScriptForDestination(coinControl->destChange);
// No coin control: send change to newly generated address.
} else {
// Note: We use a new key here to keep it from being obvious
// which side is the change. The drawback is that by not
// reusing a previous key, the change may be lost if a
// backup is restored, if the backup doesn't have the new
// private key for the change. If we reused the old key, it
// would be possible to add code to look for and rediscover
// unknown transactions that were written with keys of ours
// to recover post-backup change.
// Reserve a new key pair from key pool.
CPubKey vchPubKey;
bool ret;
ret = reservekey.GetReservedKey(vchPubKey, true);
if (!ret) {
strFailReason = _("Keypool ran out, please call "
"keypoolrefill first");
return false;
}
scriptChange = GetScriptForDestination(vchPubKey.GetID());
}
CTxOut newTxOut(nChange, scriptChange);
// 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(dustRelayFee)) {
Amount nDust = newTxOut.GetDustThreshold(dustRelayFee) -
newTxOut.nValue;
// Raise change until no more dust.
newTxOut.nValue += nDust;
// Subtract from first recipient.
for (unsigned int i = 0; i < vecSend.size(); i++) {
if (vecSend[i].fSubtractFeeFromAmount) {
txNew.vout[i].nValue -= nDust;
if (txNew.vout[i].IsDust(dustRelayFee)) {
strFailReason =
_("The transaction amount is too small "
"to send after the fee has been "
"deducted");
return false;
}
break;
}
}
}
// Never create dust outputs; if we would, just add the dust to
// the fee.
if (newTxOut.IsDust(dustRelayFee)) {
nChangePosInOut = -1;
nFeeRet += nChange;
reservekey.ReturnKey();
} else {
if (nChangePosInOut == -1) {
// Insert change txn at random position:
nChangePosInOut = GetRandInt(txNew.vout.size() + 1);
} else if ((unsigned int)nChangePosInOut >
txNew.vout.size()) {
strFailReason = _("Change index out of range");
return false;
}
std::vector<CTxOut>::iterator position =
txNew.vout.begin() + nChangePosInOut;
txNew.vout.insert(position, newTxOut);
}
} else {
reservekey.ReturnKey();
}
// Fill vin
//
// Note how the sequence number is set to non-maxint so that the
// nLockTime set above actually works.
for (const auto &coin : setCoins) {
txNew.vin.push_back(
CTxIn(coin.first->GetId(), coin.second, CScript(),
std::numeric_limits<uint32_t>::max() - 1));
}
// Fill in dummy signatures for fee calculation.
if (!DummySignTx(txNew, setCoins)) {
strFailReason = _("Signing transaction failed");
return false;
}
CTransaction txNewConst(txNew);
unsigned int nBytes = txNewConst.GetTotalSize();
dPriority = txNewConst.ComputePriority(dPriority, nBytes);
// Remove scriptSigs to eliminate the fee calculation dummy
// signatures.
for (auto &vin : txNew.vin) {
vin.scriptSig = CScript();
}
// Allow to override the default confirmation target over the
// CoinControl instance.
int currentConfirmationTarget = nTxConfirmTarget;
if (coinControl && coinControl->nConfirmTarget > 0) {
currentConfirmationTarget = coinControl->nConfirmTarget;
}
Amount nFeeNeeded =
GetMinimumFee(nBytes, currentConfirmationTarget, mempool);
if (coinControl && nFeeNeeded > Amount::zero() &&
coinControl->nMinimumTotalFee > nFeeNeeded) {
nFeeNeeded = coinControl->nMinimumTotalFee;
}
if (coinControl && coinControl->fOverrideFeeRate) {
nFeeNeeded = coinControl->nFeeRate.GetFeeCeiling(nBytes);
}
// If we made it here and we aren't even able to meet the relay fee
// on the next pass, give up because we must be at the maximum
// allowed fee.
Amount minFee = GetConfig().GetMinFeePerKB().GetFeeCeiling(nBytes);
if (nFeeNeeded < minFee) {
strFailReason = _("Transaction too large for fee policy");
return false;
}
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) {
Amount extraFeePaid = nFeeRet - nFeeNeeded;
std::vector<CTxOut>::iterator change_position =
txNew.vout.begin() + nChangePosInOut;
change_position->nValue += extraFeePaid;
nFeeRet -= extraFeePaid;
}
// Done, enough fee included.
break;
}
// Try to reduce change to include necessary fee.
if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {
Amount additionalFeeNeeded = nFeeNeeded - nFeeRet;
std::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;
// Done, able to increase fee from change.
break;
}
}
// Include more fee and try again.
nFeeRet = nFeeNeeded;
continue;
}
if (sign) {
SigHashType sigHashType = SigHashType().withForkId();
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,
sigHashType),
scriptPubKey, sigdata)) {
strFailReason = _("Signing transaction failed");
return false;
}
UpdateTransaction(txNew, nIn, sigdata);
nIn++;
}
}
// Embed the constructed transaction data in wtxNew.
wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));
// Limit size.
if (CTransaction(wtxNew).GetTotalSize() >= MAX_STANDARD_TX_SIZE) {
strFailReason = _("Transaction too large");
return false;
}
}
if (gArgs.GetBoolArg("-walletrejectlongchains",
DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits.
LockPoints lp;
CTxMemPoolEntry entry(wtxNew.tx, Amount::zero(), 0, 0, 0,
Amount::zero(), false, 0, lp);
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors =
gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
size_t nLimitAncestorSize =
gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) *
1000;
size_t nLimitDescendants =
gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
size_t nLimitDescendantSize =
gArgs.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;
}
/**
* Call after CreateTransaction unless you want to abort
*/
bool CWallet::CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey,
CConnman *connman, CValidationState &state) {
LOCK2(cs_main, cs_wallet);
LogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString());
// Take key pair from key pool so it won't be used again.
reservekey.KeepKey();
// Add tx to wallet, because if it has change it's also ours, otherwise just
// for transaction history.
AddToWallet(wtxNew);
// Notify that old coins are spent.
for (const CTxIn &txin : wtxNew.tx->vin) {
CWalletTx &coin = mapWallet[txin.prevout.GetTxId()];
coin.BindWallet(this);
NotifyTransactionChanged(this, coin.GetId(), CT_UPDATED);
}
// Track how many getdata requests our transaction gets.
mapRequestCount[wtxNew.GetId()] = 0;
if (fBroadcastTransactions) {
// Broadcast
if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) {
LogPrintf("CommitTransaction(): Transaction cannot be "
"broadcast immediately, %s\n",
state.GetRejectReason());
// TODO: if we expect the failure to be long term or permanent,
// instead delete wtx from the wallet and return failure.
} else {
wtxNew.RelayWalletTransaction(connman);
}
}
return true;
}
void CWallet::ListAccountCreditDebit(const std::string &strAccount,
std::list<CAccountingEntry> &entries) {
CWalletDB walletdb(*dbw);
return walletdb.ListAccountCreditDebit(strAccount, entries);
}
bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry) {
CWalletDB walletdb(*dbw);
return AddAccountingEntry(acentry, &walletdb);
}
bool CWallet::AddAccountingEntry(const CAccountingEntry &acentry,
CWalletDB *pwalletdb) {
if (!pwalletdb->WriteAccountingEntry(++nAccountingEntryNumber, acentry)) {
return false;
}
laccentries.push_back(acentry);
CAccountingEntry &entry = laccentries.back();
wtxOrdered.insert(std::make_pair(entry.nOrderPos, TxPair(nullptr, &entry)));
return true;
}
DBErrors CWallet::LoadWallet(bool &fFirstRunRet) {
fFirstRunRet = false;
DBErrors nLoadWalletRet = CWalletDB(*dbw, "cr+").LoadWallet(this);
if (nLoadWalletRet == DB_NEED_REWRITE) {
if (dbw->Rewrite("\x04pool")) {
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
// This wallet is in its first run if all of these are empty
fFirstRunRet = mapKeys.empty() && mapCryptedKeys.empty() &&
mapWatchKeys.empty() && setWatchOnly.empty() &&
mapScripts.empty();
if (nLoadWalletRet != DB_LOAD_OK) {
return nLoadWalletRet;
}
uiInterface.LoadWallet(this);
return DB_LOAD_OK;
}
DBErrors CWallet::ZapSelectTx(std::vector<TxId> &txIdsIn,
std::vector<TxId> &txIdsOut) {
AssertLockHeld(cs_wallet); // mapWallet
DBErrors nZapSelectTxRet =
CWalletDB(*dbw, "cr+").ZapSelectTx(txIdsIn, txIdsOut);
for (const TxId &txid : txIdsOut) {
mapWallet.erase(txid);
}
if (nZapSelectTxRet == DB_NEED_REWRITE) {
if (dbw->Rewrite("\x04pool")) {
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
if (nZapSelectTxRet != DB_LOAD_OK) {
return nZapSelectTxRet;
}
MarkDirty();
return DB_LOAD_OK;
}
DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx> &vWtx) {
DBErrors nZapWalletTxRet = CWalletDB(*dbw, "cr+").ZapWalletTx(vWtx);
if (nZapWalletTxRet == DB_NEED_REWRITE) {
if (dbw->Rewrite("\x04pool")) {
LOCK(cs_wallet);
setInternalKeyPool.clear();
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
// Note: can't top-up keypool here, because wallet is locked.
// User will be prompted to unlock wallet the next operation
// that requires a new key.
}
}
if (nZapWalletTxRet != DB_LOAD_OK) {
return nZapWalletTxRet;
}
return DB_LOAD_OK;
}
bool CWallet::SetAddressBook(const CTxDestination &address,
const std::string &strName,
const std::string &strPurpose) {
bool fUpdated = false;
{
// mapAddressBook
LOCK(cs_wallet);
std::map<CTxDestination, CAddressBookData>::iterator mi =
mapAddressBook.find(address);
fUpdated = mi != mapAddressBook.end();
mapAddressBook[address].name = strName;
// Update purpose only if requested.
if (!strPurpose.empty()) {
mapAddressBook[address].purpose = strPurpose;
}
}
NotifyAddressBookChanged(this, address, strName,
::IsMine(*this, address) != ISMINE_NO, strPurpose,
(fUpdated ? CT_UPDATED : CT_NEW));
if (!strPurpose.empty() &&
!CWalletDB(*dbw).WritePurpose(address, strPurpose)) {
return false;
}
return CWalletDB(*dbw).WriteName(address, strName);
}
bool CWallet::DelAddressBook(const CTxDestination &address) {
{
// mapAddressBook
LOCK(cs_wallet);
// Delete destdata tuples associated with address.
for (const std::pair<std::string, std::string> &item :
mapAddressBook[address].destdata) {
CWalletDB(*dbw).EraseDestData(address, item.first);
}
mapAddressBook.erase(address);
}
NotifyAddressBookChanged(this, address, "",
::IsMine(*this, address) != ISMINE_NO, "",
CT_DELETED);
CWalletDB(*dbw).ErasePurpose(address);
return CWalletDB(*dbw).EraseName(address);
}
const std::string &CWallet::GetAccountName(const CScript &scriptPubKey) const {
CTxDestination address;
if (ExtractDestination(scriptPubKey, address) &&
!scriptPubKey.IsUnspendable()) {
auto mi = mapAddressBook.find(address);
if (mi != mapAddressBook.end()) {
return mi->second.name;
}
}
// A scriptPubKey that doesn't have an entry in the address book is
// associated with the default account ("").
const static std::string DEFAULT_ACCOUNT_NAME;
return DEFAULT_ACCOUNT_NAME;
}
/**
* Mark old keypool keys as used, and generate all new keys.
*/
bool CWallet::NewKeyPool() {
LOCK(cs_wallet);
CWalletDB walletdb(*dbw);
for (int64_t nIndex : setInternalKeyPool) {
walletdb.ErasePool(nIndex);
}
setInternalKeyPool.clear();
for (int64_t nIndex : setExternalKeyPool) {
walletdb.ErasePool(nIndex);
}
setExternalKeyPool.clear();
m_pool_key_to_index.clear();
if (!TopUpKeyPool()) {
return false;
}
LogPrintf("CWallet::NewKeyPool rewrote keypool\n");
return true;
}
size_t CWallet::KeypoolCountExternalKeys() {
// setExternalKeyPool
AssertLockHeld(cs_wallet);
return setExternalKeyPool.size();
}
void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) {
AssertLockHeld(cs_wallet);
if (keypool.fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
m_max_keypool_index = std::max(m_max_keypool_index, nIndex);
m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex;
// If no metadata exists yet, create a default with the pool key's
// creation time. Note that this may be overwritten by actually
// stored metadata for that key later, which is fine.
CKeyID keyid = keypool.vchPubKey.GetID();
if (mapKeyMetadata.count(keyid) == 0) {
mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime);
}
}
bool CWallet::TopUpKeyPool(unsigned int kpSize) {
LOCK(cs_wallet);
if (IsLocked()) {
return false;
}
// Top up key pool
unsigned int nTargetSize;
if (kpSize > 0) {
nTargetSize = kpSize;
} else {
nTargetSize = std::max<int64_t>(
gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), 0);
}
// count amount of available keys (internal, external)
// make sure the keypool of external and internal keys fits the user
// selected target (-keypool)
int64_t missingExternal = std::max<int64_t>(
std::max<int64_t>(nTargetSize, 1) - setExternalKeyPool.size(), 0);
int64_t missingInternal = std::max<int64_t>(
std::max<int64_t>(nTargetSize, 1) - setInternalKeyPool.size(), 0);
if (!IsHDEnabled() || !CanSupportFeature(FEATURE_HD_SPLIT)) {
// don't create extra internal keys
missingInternal = 0;
}
bool internal = false;
CWalletDB walletdb(*dbw);
for (int64_t i = missingInternal + missingExternal; i--;) {
if (i < missingInternal) {
internal = true;
}
// How in the hell did you use so many keys?
assert(m_max_keypool_index < std::numeric_limits<int64_t>::max());
int64_t index = ++m_max_keypool_index;
CPubKey pubkey(GenerateNewKey(walletdb, internal));
if (!walletdb.WritePool(index, CKeyPool(pubkey, internal))) {
throw std::runtime_error(std::string(__func__) +
": writing generated key failed");
}
if (internal) {
setInternalKeyPool.insert(index);
} else {
setExternalKeyPool.insert(index);
}
m_pool_key_to_index[pubkey.GetID()] = index;
}
if (missingInternal + missingExternal > 0) {
LogPrintf(
"keypool added %d keys (%d internal), size=%u (%u internal)\n",
missingInternal + missingExternal, missingInternal,
setInternalKeyPool.size() + setExternalKeyPool.size(),
setInternalKeyPool.size());
}
return true;
}
void CWallet::ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool,
bool fRequestedInternal) {
nIndex = -1;
keypool.vchPubKey = CPubKey();
LOCK(cs_wallet);
if (!IsLocked()) {
TopUpKeyPool();
}
bool fReturningInternal = IsHDEnabled() &&
CanSupportFeature(FEATURE_HD_SPLIT) &&
fRequestedInternal;
std::set<int64_t> &setKeyPool =
fReturningInternal ? setInternalKeyPool : setExternalKeyPool;
// Get the oldest key
if (setKeyPool.empty()) {
return;
}
CWalletDB walletdb(*dbw);
auto it = setKeyPool.begin();
nIndex = *it;
setKeyPool.erase(it);
if (!walletdb.ReadPool(nIndex, keypool)) {
throw std::runtime_error(std::string(__func__) + ": read failed");
}
if (!HaveKey(keypool.vchPubKey.GetID())) {
throw std::runtime_error(std::string(__func__) +
": unknown key in key pool");
}
if (keypool.fInternal != fReturningInternal) {
throw std::runtime_error(std::string(__func__) +
": keypool entry misclassified");
}
assert(keypool.vchPubKey.IsValid());
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
LogPrintf("keypool reserve %d\n", nIndex);
}
void CWallet::KeepKey(int64_t nIndex) {
// Remove from key pool.
CWalletDB walletdb(*dbw);
walletdb.ErasePool(nIndex);
LogPrintf("keypool keep %d\n", nIndex);
}
void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey) {
// Return to key pool
{
LOCK(cs_wallet);
if (fInternal) {
setInternalKeyPool.insert(nIndex);
} else {
setExternalKeyPool.insert(nIndex);
}
m_pool_key_to_index[pubkey.GetID()] = nIndex;
}
LogPrintf("keypool return %d\n", nIndex);
}
bool CWallet::GetKeyFromPool(CPubKey &result, bool internal) {
CKeyPool keypool;
LOCK(cs_wallet);
int64_t nIndex = 0;
ReserveKeyFromKeyPool(nIndex, keypool, internal);
if (nIndex == -1) {
if (IsLocked()) {
return false;
}
CWalletDB walletdb(*dbw);
result = GenerateNewKey(walletdb, internal);
return true;
}
KeepKey(nIndex);
result = keypool.vchPubKey;
return true;
}
static int64_t GetOldestKeyTimeInPool(const std::set<int64_t> &setKeyPool,
CWalletDB &walletdb) {
if (setKeyPool.empty()) {
return GetTime();
}
CKeyPool keypool;
int64_t nIndex = *(setKeyPool.begin());
if (!walletdb.ReadPool(nIndex, keypool)) {
throw std::runtime_error(std::string(__func__) +
": read oldest key in keypool failed");
}
assert(keypool.vchPubKey.IsValid());
return keypool.nTime;
}
int64_t CWallet::GetOldestKeyPoolTime() {
LOCK(cs_wallet);
CWalletDB walletdb(*dbw);
// load oldest key from keypool, get time and return
int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, walletdb);
if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) {
oldestKey = std::max(
GetOldestKeyTimeInPool(setInternalKeyPool, walletdb), oldestKey);
}
return oldestKey;
}
std::map<CTxDestination, Amount> CWallet::GetAddressBalances() {
std::map<CTxDestination, Amount> balances;
LOCK(cs_wallet);
for (std::pair<TxId, CWalletTx> walletEntry : mapWallet) {
CWalletTx *pcoin = &walletEntry.second;
if (!pcoin->IsTrusted()) {
continue;
}
- if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) {
+ if (pcoin->IsImmatureCoinBase()) {
continue;
}
int nDepth = pcoin->GetDepthInMainChain();
if (nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) {
continue;
}
for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) {
CTxDestination addr;
if (!IsMine(pcoin->tx->vout[i])) {
continue;
}
if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) {
continue;
}
Amount n = IsSpent(walletEntry.first, i)
? Amount::zero()
: pcoin->tx->vout[i].nValue;
if (!balances.count(addr)) {
balances[addr] = Amount::zero();
}
balances[addr] += n;
}
}
return balances;
}
std::set<std::set<CTxDestination>> CWallet::GetAddressGroupings() {
// mapWallet
AssertLockHeld(cs_wallet);
std::set<std::set<CTxDestination>> groupings;
std::set<CTxDestination> grouping;
for (std::pair<uint256, CWalletTx> walletEntry : mapWallet) {
CWalletTx *pcoin = &walletEntry.second;
if (pcoin->tx->vin.size() > 0) {
bool any_mine = false;
// Group all input addresses with each other.
for (CTxIn txin : pcoin->tx->vin) {
CTxDestination address;
// If this input isn't mine, ignore it.
if (!IsMine(txin)) {
continue;
}
if (!ExtractDestination(mapWallet[txin.prevout.GetTxId()]
.tx->vout[txin.prevout.GetN()]
.scriptPubKey,
address)) {
continue;
}
grouping.insert(address);
any_mine = true;
}
// Group change with input addresses.
if (any_mine) {
for (CTxOut txout : pcoin->tx->vout) {
if (IsChange(txout)) {
CTxDestination txoutAddr;
if (!ExtractDestination(txout.scriptPubKey,
txoutAddr)) {
continue;
}
grouping.insert(txoutAddr);
}
}
}
if (grouping.size() > 0) {
groupings.insert(grouping);
grouping.clear();
}
}
// Group lone addrs by themselves.
for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)
if (IsMine(pcoin->tx->vout[i])) {
CTxDestination address;
if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey,
address)) {
continue;
}
grouping.insert(address);
groupings.insert(grouping);
grouping.clear();
}
}
// A set of pointers to groups of addresses.
std::set<std::set<CTxDestination> *> uniqueGroupings;
// Map addresses to the unique group containing it.
std::map<CTxDestination, std::set<CTxDestination> *> setmap;
for (std::set<CTxDestination> _grouping : groupings) {
// Make a set of all the groups hit by this new group.
std::set<std::set<CTxDestination> *> hits;
std::map<CTxDestination, std::set<CTxDestination> *>::iterator it;
for (CTxDestination address : _grouping) {
if ((it = setmap.find(address)) != setmap.end()) {
hits.insert((*it).second);
}
}
// Merge all hit groups into a new single group and delete old groups.
std::set<CTxDestination> *merged =
new std::set<CTxDestination>(_grouping);
for (std::set<CTxDestination> *hit : hits) {
merged->insert(hit->begin(), hit->end());
uniqueGroupings.erase(hit);
delete hit;
}
uniqueGroupings.insert(merged);
// Update setmap.
for (CTxDestination element : *merged) {
setmap[element] = merged;
}
}
std::set<std::set<CTxDestination>> ret;
for (std::set<CTxDestination> *uniqueGrouping : uniqueGroupings) {
ret.insert(*uniqueGrouping);
delete uniqueGrouping;
}
return ret;
}
std::set<CTxDestination>
CWallet::GetAccountAddresses(const std::string &strAccount) const {
LOCK(cs_wallet);
std::set<CTxDestination> result;
for (const std::pair<CTxDestination, CAddressBookData> &item :
mapAddressBook) {
const CTxDestination &address = item.first;
const std::string &strName = item.second.name;
if (strName == strAccount) {
result.insert(address);
}
}
return result;
}
bool CReserveKey::GetReservedKey(CPubKey &pubkey, bool internal) {
if (nIndex == -1) {
CKeyPool keypool;
pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal);
if (nIndex == -1) {
return false;
}
vchPubKey = keypool.vchPubKey;
fInternal = keypool.fInternal;
}
assert(vchPubKey.IsValid());
pubkey = vchPubKey;
return true;
}
void CReserveKey::KeepKey() {
if (nIndex != -1) {
pwallet->KeepKey(nIndex);
}
nIndex = -1;
vchPubKey = CPubKey();
}
void CReserveKey::ReturnKey() {
if (nIndex != -1) {
pwallet->ReturnKey(nIndex, fInternal, vchPubKey);
}
nIndex = -1;
vchPubKey = CPubKey();
}
void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) {
AssertLockHeld(cs_wallet);
bool internal = setInternalKeyPool.count(keypool_id);
if (!internal) {
assert(setExternalKeyPool.count(keypool_id));
}
std::set<int64_t> *setKeyPool =
internal ? &setInternalKeyPool : &setExternalKeyPool;
auto it = setKeyPool->begin();
CWalletDB walletdb(*dbw);
while (it != std::end(*setKeyPool)) {
const int64_t &index = *(it);
if (index > keypool_id) {
// set*KeyPool is ordered
break;
}
CKeyPool keypool;
if (walletdb.ReadPool(index, keypool)) {
// TODO: This should be unnecessary
m_pool_key_to_index.erase(keypool.vchPubKey.GetID());
}
walletdb.ErasePool(index);
it = setKeyPool->erase(it);
}
}
bool CWallet::HasUnusedKeys(size_t min_keys) const {
return setExternalKeyPool.size() >= min_keys &&
(setInternalKeyPool.size() >= min_keys ||
!CanSupportFeature(FEATURE_HD_SPLIT));
}
void CWallet::GetScriptForMining(std::shared_ptr<CReserveScript> &script) {
std::shared_ptr<CReserveKey> rKey = std::make_shared<CReserveKey>(this);
CPubKey pubkey;
if (!rKey->GetReservedKey(pubkey)) {
return;
}
script = rKey;
script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
}
void CWallet::LockCoin(const COutPoint &output) {
// setLockedCoins
AssertLockHeld(cs_wallet);
setLockedCoins.insert(output);
}
void CWallet::UnlockCoin(const COutPoint &output) {
// setLockedCoins
AssertLockHeld(cs_wallet);
setLockedCoins.erase(output);
}
void CWallet::UnlockAllCoins() {
// setLockedCoins
AssertLockHeld(cs_wallet);
setLockedCoins.clear();
}
bool CWallet::IsLockedCoin(const TxId &txid, uint32_t n) const {
// setLockedCoins
AssertLockHeld(cs_wallet);
COutPoint outpt(txid, n);
return setLockedCoins.count(outpt) > 0;
}
void CWallet::ListLockedCoins(std::vector<COutPoint> &vOutpts) {
// setLockedCoins
AssertLockHeld(cs_wallet);
for (COutPoint outpoint : setLockedCoins) {
vOutpts.push_back(outpoint);
}
}
/** @} */ // end of Actions
void CWallet::GetKeyBirthTimes(
std::map<CTxDestination, int64_t> &mapKeyBirth) const {
// mapKeyMetadata
AssertLockHeld(cs_wallet);
mapKeyBirth.clear();
// Get birth times for keys with metadata.
for (const auto &entry : mapKeyMetadata) {
if (entry.second.nCreateTime) {
mapKeyBirth[entry.first] = entry.second.nCreateTime;
}
}
// Map in which we'll infer heights of other keys the tip can be
// reorganized; use a 144-block safety margin.
CBlockIndex *pindexMax =
chainActive[std::max(0, chainActive.Height() - 144)];
std::map<CKeyID, CBlockIndex *> mapKeyFirstBlock;
std::set<CKeyID> setKeys;
GetKeys(setKeys);
for (const CKeyID &keyid : setKeys) {
if (mapKeyBirth.count(keyid) == 0) {
mapKeyFirstBlock[keyid] = pindexMax;
}
}
setKeys.clear();
// If there are no such keys, we're done.
if (mapKeyFirstBlock.empty()) {
return;
}
// Find first block that affects those keys, if there are any left.
std::vector<CKeyID> vAffected;
for (std::map<TxId, CWalletTx>::const_iterator it = mapWallet.begin();
it != mapWallet.end(); it++) {
// Iterate over all wallet transactions...
const CWalletTx &wtx = (*it).second;
BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);
if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {
// ... which are already in a block.
int nHeight = blit->second->nHeight;
for (const CTxOut &txout : wtx.tx->vout) {
// Iterate over all their outputs...
CAffectedKeysVisitor(*this, vAffected)
.Process(txout.scriptPubKey);
for (const CKeyID &keyid : vAffected) {
// ... and all their affected keys.
std::map<CKeyID, CBlockIndex *>::iterator rit =
mapKeyFirstBlock.find(keyid);
if (rit != mapKeyFirstBlock.end() &&
nHeight < rit->second->nHeight) {
rit->second = blit->second;
}
}
vAffected.clear();
}
}
}
// Extract block timestamps for those keys.
for (const std::pair<CKeyID, CBlockIndex *> &p : mapKeyFirstBlock) {
// Block times can be 2h off.
mapKeyBirth[p.first] = p.second->GetBlockTime() - TIMESTAMP_WINDOW;
}
}
/**
* Compute smart timestamp for a transaction being added to the wallet.
*
* Logic:
* - If sending a transaction, assign its timestamp to the current time.
* - If receiving a transaction outside a block, assign its timestamp to the
* current time.
* - If receiving a block with a future timestamp, assign all its (not already
* known) transactions' timestamps to the current time.
* - If receiving a block with a past timestamp, before the most recent known
* transaction (that we care about), assign all its (not already known)
* transactions' timestamps to the same timestamp as that most-recent-known
* transaction.
* - If receiving a block with a past timestamp, but after the most recent known
* transaction, assign all its (not already known) transactions' timestamps to
* the block time.
*
* For more information see CWalletTx::nTimeSmart,
* https://bitcointalk.org/?topic=54527, or
* https://github.com/bitcoin/bitcoin/pull/1393.
*/
unsigned int CWallet::ComputeTimeSmart(const CWalletTx &wtx) const {
unsigned int nTimeSmart = wtx.nTimeReceived;
if (!wtx.hashUnset()) {
if (mapBlockIndex.count(wtx.hashBlock)) {
int64_t latestNow = wtx.nTimeReceived;
int64_t latestEntry = 0;
// Tolerate times up to the last timestamp in the wallet not more
// than 5 minutes into the future
int64_t latestTolerated = latestNow + 300;
const TxItems &txOrdered = wtxOrdered;
for (auto it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) {
CWalletTx *const pwtx = it->second.first;
if (pwtx == &wtx) {
continue;
}
CAccountingEntry *const pacentry = it->second.second;
int64_t nSmartTime;
if (pwtx) {
nSmartTime = pwtx->nTimeSmart;
if (!nSmartTime) {
nSmartTime = pwtx->nTimeReceived;
}
} else {
nSmartTime = pacentry->nTime;
}
if (nSmartTime <= latestTolerated) {
latestEntry = nSmartTime;
if (nSmartTime > latestNow) {
latestNow = nSmartTime;
}
break;
}
}
int64_t blocktime = mapBlockIndex[wtx.hashBlock]->GetBlockTime();
nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));
} else {
LogPrintf("%s: found %s in block %s not in index\n", __func__,
wtx.GetId().ToString(), wtx.hashBlock.ToString());
}
}
return nTimeSmart;
}
bool CWallet::AddDestData(const CTxDestination &dest, const std::string &key,
const std::string &value) {
if (boost::get<CNoDestination>(&dest)) {
return false;
}
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
return CWalletDB(*dbw).WriteDestData(dest, key, value);
}
bool CWallet::EraseDestData(const CTxDestination &dest,
const std::string &key) {
if (!mapAddressBook[dest].destdata.erase(key)) {
return false;
}
return CWalletDB(*dbw).EraseDestData(dest, key);
}
bool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key,
const std::string &value) {
mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
return true;
}
bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key,
std::string *value) const {
std::map<CTxDestination, CAddressBookData>::const_iterator i =
mapAddressBook.find(dest);
if (i != mapAddressBook.end()) {
CAddressBookData::StringMap::const_iterator j =
i->second.destdata.find(key);
if (j != i->second.destdata.end()) {
if (value) {
*value = j->second;
}
return true;
}
}
return false;
}
CWallet *CWallet::CreateWalletFromFile(const CChainParams &chainParams,
const std::string walletFile) {
// Needed to restore wallet transaction meta data after -zapwallettxes
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWalletDBWrapper> dbw(
new CWalletDBWrapper(&bitdb, walletFile));
CWallet *tempWallet = new CWallet(chainParams, std::move(dbw));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DB_LOAD_OK) {
InitError(
strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
delete tempWallet;
tempWallet = nullptr;
}
uiInterface.InitMessage(_("Loading wallet..."));
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
std::unique_ptr<CWalletDBWrapper> dbw(
new CWalletDBWrapper(&bitdb, walletFile));
CWallet *walletInstance = new CWallet(chainParams, std::move(dbw));
DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);
if (nLoadWalletRet != DB_LOAD_OK) {
if (nLoadWalletRet == DB_CORRUPT) {
InitError(
strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
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) {
InitError(strprintf(
_("Error loading %s: Wallet requires newer version of %s"),
walletFile, _(PACKAGE_NAME)));
return nullptr;
} else if (nLoadWalletRet == DB_NEED_REWRITE) {
InitError(strprintf(
_("Wallet needed to be rewritten: restart %s to complete"),
_(PACKAGE_NAME)));
return nullptr;
} else {
InitError(strprintf(_("Error loading %s"), walletFile));
return nullptr;
}
}
if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) {
int nMaxVersion = gArgs.GetArg("-upgradewallet", 0);
// The -upgradewallet without argument case
if (nMaxVersion == 0) {
LogPrintf("Performing wallet upgrade to %i\n", FEATURE_LATEST);
nMaxVersion = CLIENT_VERSION;
// permanently upgrade the wallet immediately
walletInstance->SetMinVersion(FEATURE_LATEST);
} else {
LogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
}
if (nMaxVersion < walletInstance->GetVersion()) {
InitError(_("Cannot downgrade wallet"));
return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
}
if (fFirstRun) {
// Create new keyUser and set as default key.
if (gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) &&
!walletInstance->IsHDEnabled()) {
// Ensure this wallet.dat can only be opened by clients supporting
// HD with chain split.
walletInstance->SetMinVersion(FEATURE_HD_SPLIT);
// Generate a new master key.
CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();
if (!walletInstance->SetHDMasterKey(masterPubKey)) {
throw std::runtime_error(std::string(__func__) +
": Storing master key failed");
}
}
// Top up the keypool
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate initial keys") += "\n");
return nullptr;
}
walletInstance->SetBestChain(chainActive.GetLocator());
} else if (gArgs.IsArgSet("-usehd")) {
bool useHD = gArgs.GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);
if (walletInstance->IsHDEnabled() && !useHD) {
InitError(strprintf(_("Error loading %s: You can't disable HD on a "
"already existing HD wallet"),
walletFile));
return nullptr;
}
if (!walletInstance->IsHDEnabled() && useHD) {
InitError(strprintf(_("Error loading %s: You can't enable HD on a "
"already existing non-HD wallet"),
walletFile));
return nullptr;
}
}
LogPrintf(" wallet %15dms\n", GetTimeMillis() - nStart);
RegisterValidationInterface(walletInstance);
// Try to top up keypool. No-op if the wallet is locked.
walletInstance->TopUpKeyPool();
CBlockIndex *pindexRescan = chainActive.Genesis();
if (!gArgs.GetBoolArg("-rescan", false)) {
CWalletDB walletdb(*walletInstance->dbw);
CBlockLocator locator;
if (walletdb.ReadBestBlock(locator)) {
pindexRescan = FindForkInGlobalIndex(chainActive, locator);
}
}
if (chainActive.Tip() && chainActive.Tip() != pindexRescan) {
// We can't rescan beyond non-pruned blocks, stop and throw an error.
// This might happen if a user uses a old wallet within a pruned node or
// if he ran -disablewallet for a longer time, then decided to
// re-enable.
if (fPruneMode) {
CBlockIndex *block = chainActive.Tip();
while (block && block->pprev && block->pprev->nStatus.hasData() &&
block->pprev->nTx > 0 && pindexRescan != block) {
block = block->pprev;
}
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 nullptr;
}
}
uiInterface.InitMessage(_("Rescanning..."));
LogPrintf("Rescanning last %i blocks (from block %i)...\n",
chainActive.Height() - pindexRescan->nHeight,
pindexRescan->nHeight);
// No need to read and scan block if block was created before our wallet
// birthday (as adjusted for block time variability)
while (pindexRescan && walletInstance->nTimeFirstKey &&
(pindexRescan->GetBlockTime() <
(walletInstance->nTimeFirstKey - TIMESTAMP_WINDOW))) {
pindexRescan = chainActive.Next(pindexRescan);
}
nStart = GetTimeMillis();
walletInstance->ScanForWalletTransactions(pindexRescan, nullptr, true);
LogPrintf(" rescan %15dms\n", GetTimeMillis() - nStart);
walletInstance->SetBestChain(chainActive.GetLocator());
walletInstance->dbw->IncrementUpdateCounter();
// Restore wallet transaction metadata after -zapwallettxes=1
if (gArgs.GetBoolArg("-zapwallettxes", false) &&
gArgs.GetArg("-zapwallettxes", "1") != "2") {
CWalletDB walletdb(*walletInstance->dbw);
for (const CWalletTx &wtxOld : vWtx) {
const TxId txid = wtxOld.GetId();
std::map<TxId, CWalletTx>::iterator mi =
walletInstance->mapWallet.find(txid);
if (mi != walletInstance->mapWallet.end()) {
const CWalletTx *copyFrom = &wtxOld;
CWalletTx *copyTo = &mi->second;
copyTo->mapValue = copyFrom->mapValue;
copyTo->vOrderForm = copyFrom->vOrderForm;
copyTo->nTimeReceived = copyFrom->nTimeReceived;
copyTo->nTimeSmart = copyFrom->nTimeSmart;
copyTo->fFromMe = copyFrom->fFromMe;
copyTo->strFromAccount = copyFrom->strFromAccount;
copyTo->nOrderPos = copyFrom->nOrderPos;
walletdb.WriteTx(*copyTo);
}
}
}
}
walletInstance->SetBroadcastTransactions(
gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));
LOCK(walletInstance->cs_wallet);
LogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
LogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
LogPrintf("mapAddressBook.size() = %u\n",
walletInstance->mapAddressBook.size());
return walletInstance;
}
std::atomic<bool> CWallet::fFlushScheduled(false);
void CWallet::postInitProcess(CScheduler &scheduler) {
// Add wallet transactions that aren't already in a block to mempool.
// Do this here as mempool requires genesis block to be loaded.
ReacceptWalletTransactions();
// Run a thread to flush wallet periodically.
if (!CWallet::fFlushScheduled.exchange(true)) {
scheduler.scheduleEvery(MaybeCompactWalletDB, 500);
}
}
bool CWallet::BackupWallet(const std::string &strDest) {
return dbw->Backup(strDest);
}
CKeyPool::CKeyPool() {
nTime = GetTime();
fInternal = false;
}
CKeyPool::CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn) {
nTime = GetTime();
vchPubKey = vchPubKeyIn;
fInternal = internalIn;
}
CWalletKey::CWalletKey(int64_t nExpires) {
nTimeCreated = (nExpires ? GetTime() : 0);
nTimeExpires = nExpires;
}
void CMerkleTx::SetMerkleBranch(const CBlockIndex *pindex, int posInBlock) {
// Update the tx's hashBlock
hashBlock = pindex->GetBlockHash();
// Set the position of the transaction in the block.
nIndex = posInBlock;
}
int CMerkleTx::GetDepthInMainChain(const CBlockIndex *&pindexRet) const {
if (hashUnset()) {
return 0;
}
AssertLockHeld(cs_main);
// Find the block it claims to be in.
BlockMap::iterator mi = mapBlockIndex.find(hashBlock);
if (mi == mapBlockIndex.end()) {
return 0;
}
CBlockIndex *pindex = (*mi).second;
if (!pindex || !chainActive.Contains(pindex)) {
return 0;
}
pindexRet = pindex;
return ((nIndex == -1) ? (-1) : 1) *
(chainActive.Height() - pindex->nHeight + 1);
}
int CMerkleTx::GetBlocksToMaturity() const {
if (!IsCoinBase()) {
return 0;
}
return std::max(0, (COINBASE_MATURITY + 1) - GetDepthInMainChain());
}
+bool CMerkleTx::IsImmatureCoinBase() const {
+ // note GetBlocksToMaturity is 0 for non-coinbase tx
+ return GetBlocksToMaturity() > 0;
+}
+
bool CMerkleTx::AcceptToMemoryPool(const Amount nAbsurdFee,
CValidationState &state) {
return ::AcceptToMemoryPool(GetConfig(), mempool, state, tx, true, nullptr,
false, nAbsurdFee);
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a2742f578..be930eb22 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -1,1206 +1,1212 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Copyright (c) 2018 The Bitcoin developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_WALLET_WALLET_H
#define BITCOIN_WALLET_WALLET_H
#include "amount.h"
#include "script/ismine.h"
#include "script/sign.h"
#include "streams.h"
#include "tinyformat.h"
#include "ui_interface.h"
#include "utilstrencodings.h"
#include "validationinterface.h"
#include "wallet/crypter.h"
#include "wallet/rpcwallet.h"
#include "wallet/walletdb.h"
#include <boost/thread.hpp>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <map>
#include <set>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
typedef CWallet *CWalletRef;
extern std::vector<CWalletRef> vpwallets;
/**
* Settings
*/
extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
static const Amount DEFAULT_TRANSACTION_FEE = Amount::zero();
//! -fallbackfee default
static const Amount DEFAULT_FALLBACK_FEE(20000 * SATOSHI);
//! minimum recommended increment for BIP 125 replacement txs
static const Amount WALLET_INCREMENTAL_RELAY_FEE(5000 * SATOSHI);
//! target minimum change amount
static const Amount MIN_CHANGE = CENT;
//! final minimum change amount after paying for fees
static const Amount MIN_FINAL_CHANGE = MIN_CHANGE / 2;
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -walletrejectlongchains
static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
//! Largest (in bytes) free transaction we're willing to create
static const unsigned int MAX_FREE_TRANSACTION_CREATE_SIZE = 1000;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;
//! if set, all keys will be derived by using BIP32
static const bool DEFAULT_USE_HD_WALLET = true;
extern const char *DEFAULT_WALLET_DAT;
static const int64_t TIMESTAMP_MIN = 0;
class CBlockIndex;
class CChainParams;
class CCoinControl;
class COutput;
class CReserveKey;
class CScript;
class CScheduler;
class CTxMemPool;
class CWalletTx;
/** (client) version numbers for particular wallet features */
enum WalletFeature {
// the earliest version new wallets supports (only useful for getinfo's
// clientversion output)
FEATURE_BASE = 10500,
// wallet encryption
FEATURE_WALLETCRYPT = 40000,
// compressed public keys
FEATURE_COMPRPUBKEY = 60000,
// Hierarchical key derivation after BIP32 (HD Wallet)
FEATURE_HD = 130000,
// Wallet with HD chain split (change outputs will use m/0'/1'/k)
FEATURE_HD_SPLIT = 160300,
// HD is optional, use FEATURE_COMPRPUBKEY as latest version
FEATURE_LATEST = FEATURE_COMPRPUBKEY,
};
/** A key pool entry */
class CKeyPool {
public:
int64_t nTime;
CPubKey vchPubKey;
bool fInternal; // for change outputs
CKeyPool();
CKeyPool(const CPubKey &vchPubKeyIn, bool internalIn);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(nVersion);
}
READWRITE(nTime);
READWRITE(vchPubKey);
if (ser_action.ForRead()) {
try {
READWRITE(fInternal);
} catch (std::ios_base::failure &) {
/**
* flag as external address if we can't read the internal
* boolean
* (this will be the case for any wallet before the HD chain
* split version)
*/
fInternal = false;
}
} else {
READWRITE(fInternal);
}
}
};
/** Address book data */
class CAddressBookData {
public:
std::string name;
std::string purpose;
CAddressBookData() { purpose = "unknown"; }
typedef std::map<std::string, std::string> StringMap;
StringMap destdata;
};
struct CRecipient {
CScript scriptPubKey;
Amount nAmount;
bool fSubtractFeeFromAmount;
};
typedef std::map<std::string, std::string> mapValue_t;
static inline void ReadOrderPos(int64_t &nOrderPos, mapValue_t &mapValue) {
if (!mapValue.count("n")) {
// TODO: calculate elsewhere
nOrderPos = -1;
return;
}
nOrderPos = atoi64(mapValue["n"].c_str());
}
static inline void WriteOrderPos(const int64_t &nOrderPos,
mapValue_t &mapValue) {
if (nOrderPos == -1) return;
mapValue["n"] = i64tostr(nOrderPos);
}
struct COutputEntry {
CTxDestination destination;
Amount amount;
int vout;
};
/** A transaction with a merkle branch linking it to the block chain. */
class CMerkleTx {
private:
/** Constant used in hashBlock to indicate tx has been abandoned */
static const uint256 ABANDON_HASH;
public:
CTransactionRef tx;
uint256 hashBlock;
/**
* An nIndex == -1 means that hashBlock (in nonzero) refers to the earliest
* block in the chain we know this or any in-wallet dependency conflicts
* with. Older clients interpret nIndex == -1 as unconfirmed for backward
* compatibility.
*/
int nIndex;
CMerkleTx() {
SetTx(MakeTransactionRef());
Init();
}
CMerkleTx(CTransactionRef arg) {
SetTx(std::move(arg));
Init();
}
/**
* Helper conversion operator to allow passing CMerkleTx where CTransaction
* is expected.
* TODO: adapt callers and remove this operator.
*/
operator const CTransaction &() const { return *tx; }
void Init() {
hashBlock = uint256();
nIndex = -1;
}
void SetTx(CTransactionRef arg) { tx = std::move(arg); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
// For compatibility with older versions.
std::vector<uint256> vMerkleBranch;
READWRITE(tx);
READWRITE(hashBlock);
READWRITE(vMerkleBranch);
READWRITE(nIndex);
}
void SetMerkleBranch(const CBlockIndex *pIndex, int posInBlock);
/**
* Return depth of transaction in blockchain:
* <0 : conflicts with a transaction this deep in the blockchain
* 0 : in memory pool, waiting to be included in a block
* >=1 : this many blocks deep in the main chain
*/
int GetDepthInMainChain(const CBlockIndex *&pindexRet) const;
int GetDepthInMainChain() const {
const CBlockIndex *pindexRet;
return GetDepthInMainChain(pindexRet);
}
bool IsInMainChain() const {
const CBlockIndex *pindexRet;
return GetDepthInMainChain(pindexRet) > 0;
}
+ /**
+ * @return number of blocks to maturity for this transaction:
+ * 0 : is not a coinbase transaction, or is a mature coinbase transaction
+ * >0 : is a coinbase transaction which matures in this many blocks
+ */
int GetBlocksToMaturity() const;
/**
* Pass this transaction to the mempool. Fails if absolute fee exceeds
* absurd fee.
*/
bool AcceptToMemoryPool(const Amount nAbsurdFee, CValidationState &state);
bool hashUnset() const {
return (hashBlock.IsNull() || hashBlock == ABANDON_HASH);
}
bool isAbandoned() const { return (hashBlock == ABANDON_HASH); }
void setAbandoned() { hashBlock = ABANDON_HASH; }
TxId GetId() const { return tx->GetId(); }
bool IsCoinBase() const { return tx->IsCoinBase(); }
+ bool IsImmatureCoinBase() const;
};
/**
* A transaction with a bunch of additional info that only the owner cares
* about. It includes any unrecorded transactions needed to link it back to the
* block chain.
*/
class CWalletTx : public CMerkleTx {
private:
const CWallet *pwallet;
public:
mapValue_t mapValue;
std::vector<std::pair<std::string, std::string>> vOrderForm;
unsigned int fTimeReceivedIsTxTime;
//!< time received by this node
unsigned int nTimeReceived;
/**
* Stable timestamp that never changes, and reflects the order a transaction
* was added to the wallet. Timestamp is based on the block time for a
* transaction added as part of a block, or else the time when the
* transaction was received if it wasn't part of a block, with the timestamp
* adjusted in both cases so timestamp order matches the order transactions
* were added to the wallet. More details can be found in
* CWallet::ComputeTimeSmart().
*/
unsigned int nTimeSmart;
/**
* From me flag is set to 1 for transactions that were created by the wallet
* on this bitcoin node, and set to 0 for transactions that were created
* externally and came in through the network or sendrawtransaction RPC.
*/
char fFromMe;
std::string strFromAccount;
//!< position in ordered transaction list
int64_t nOrderPos;
// memory only
mutable bool fDebitCached;
mutable bool fCreditCached;
mutable bool fImmatureCreditCached;
mutable bool fAvailableCreditCached;
mutable bool fWatchDebitCached;
mutable bool fWatchCreditCached;
mutable bool fImmatureWatchCreditCached;
mutable bool fAvailableWatchCreditCached;
mutable bool fChangeCached;
mutable Amount nDebitCached;
mutable Amount nCreditCached;
mutable Amount nImmatureCreditCached;
mutable Amount nAvailableCreditCached;
mutable Amount nWatchDebitCached;
mutable Amount nWatchCreditCached;
mutable Amount nImmatureWatchCreditCached;
mutable Amount nAvailableWatchCreditCached;
mutable Amount nChangeCached;
CWalletTx() { Init(nullptr); }
CWalletTx(const CWallet *pwalletIn, CTransactionRef arg)
: CMerkleTx(std::move(arg)) {
Init(pwalletIn);
}
void Init(const CWallet *pwalletIn) {
pwallet = pwalletIn;
mapValue.clear();
vOrderForm.clear();
fTimeReceivedIsTxTime = false;
nTimeReceived = 0;
nTimeSmart = 0;
fFromMe = false;
strFromAccount.clear();
fDebitCached = false;
fCreditCached = false;
fImmatureCreditCached = false;
fAvailableCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fChangeCached = false;
nDebitCached = Amount::zero();
nCreditCached = Amount::zero();
nImmatureCreditCached = Amount::zero();
nAvailableCreditCached = Amount::zero();
nWatchDebitCached = Amount::zero();
nWatchCreditCached = Amount::zero();
nAvailableWatchCreditCached = Amount::zero();
nImmatureWatchCreditCached = Amount::zero();
nChangeCached = Amount::zero();
nOrderPos = -1;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
if (ser_action.ForRead()) {
Init(nullptr);
}
char fSpent = false;
if (!ser_action.ForRead()) {
mapValue["fromaccount"] = strFromAccount;
WriteOrderPos(nOrderPos, mapValue);
if (nTimeSmart) {
mapValue["timesmart"] = strprintf("%u", nTimeSmart);
}
}
READWRITE(*(CMerkleTx *)this);
//!< Used to be vtxPrev
std::vector<CMerkleTx> vUnused;
READWRITE(vUnused);
READWRITE(mapValue);
READWRITE(vOrderForm);
READWRITE(fTimeReceivedIsTxTime);
READWRITE(nTimeReceived);
READWRITE(fFromMe);
READWRITE(fSpent);
if (ser_action.ForRead()) {
strFromAccount = mapValue["fromaccount"];
ReadOrderPos(nOrderPos, mapValue);
nTimeSmart = mapValue.count("timesmart")
? (unsigned int)atoi64(mapValue["timesmart"])
: 0;
}
mapValue.erase("fromaccount");
mapValue.erase("version");
mapValue.erase("spent");
mapValue.erase("n");
mapValue.erase("timesmart");
}
//! make sure balances are recalculated
void MarkDirty() {
fCreditCached = false;
fAvailableCreditCached = false;
fImmatureCreditCached = false;
fWatchDebitCached = false;
fWatchCreditCached = false;
fAvailableWatchCreditCached = false;
fImmatureWatchCreditCached = false;
fDebitCached = false;
fChangeCached = false;
}
void BindWallet(CWallet *pwalletIn) {
pwallet = pwalletIn;
MarkDirty();
}
//! filter decides which addresses will count towards the debit
Amount GetDebit(const isminefilter &filter) const;
Amount GetCredit(const isminefilter &filter) const;
Amount GetImmatureCredit(bool fUseCache = true) const;
Amount GetAvailableCredit(bool fUseCache = true) const;
Amount GetImmatureWatchOnlyCredit(const bool &fUseCache = true) const;
Amount GetAvailableWatchOnlyCredit(const bool &fUseCache = true) const;
Amount GetChange() const;
void GetAmounts(std::list<COutputEntry> &listReceived,
std::list<COutputEntry> &listSent, Amount &nFee,
std::string &strSentAccount,
const isminefilter &filter) const;
bool IsFromMe(const isminefilter &filter) const {
return GetDebit(filter) > Amount::zero();
}
// True if only scriptSigs are different
bool IsEquivalentTo(const CWalletTx &tx) const;
bool InMempool() const;
bool IsTrusted() const;
int64_t GetTxTime() const;
int GetRequestCount() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(CConnman *connman);
std::set<TxId> GetConflicts() const;
};
class COutput {
public:
const CWalletTx *tx;
int i;
int nDepth;
/** Whether we have the private keys to spend this output */
bool fSpendable;
/** Whether we know how to spend this output, ignoring the lack of keys */
bool fSolvable;
/**
* Whether this output is considered safe to spend. Unconfirmed transactions
* from outside keys are considered unsafe and will not be used to fund new
* spending transactions.
*/
bool fSafe;
COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn,
bool fSolvableIn, bool fSafeIn) {
tx = txIn;
i = iIn;
nDepth = nDepthIn;
fSpendable = fSpendableIn;
fSolvable = fSolvableIn;
fSafe = fSafeIn;
}
std::string ToString() const;
};
/** Private key that includes an expiration date in case it never gets used. */
class CWalletKey {
public:
CPrivKey vchPrivKey;
int64_t nTimeCreated;
int64_t nTimeExpires;
std::string strComment;
//! todo: add something to note what created it (user, getnewaddress,
//! change) maybe should have a map<string, string> property map
CWalletKey(int64_t nExpires = 0);
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) READWRITE(nVersion);
READWRITE(vchPrivKey);
READWRITE(nTimeCreated);
READWRITE(nTimeExpires);
READWRITE(LIMITED_STRING(strComment, 65536));
}
};
/**
* Internal transfers.
* Database key is acentry<account><counter>.
*/
class CAccountingEntry {
public:
std::string strAccount;
Amount nCreditDebit;
int64_t nTime;
std::string strOtherAccount;
std::string strComment;
mapValue_t mapValue;
//!< position in ordered transaction list
int64_t nOrderPos;
uint64_t nEntryNo;
CAccountingEntry() { SetNull(); }
void SetNull() {
nCreditDebit = Amount::zero();
nTime = 0;
strAccount.clear();
strOtherAccount.clear();
strComment.clear();
nOrderPos = -1;
nEntryNo = 0;
}
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) READWRITE(nVersion);
//! Note: strAccount is serialized as part of the key, not here.
READWRITE(nCreditDebit);
READWRITE(nTime);
READWRITE(LIMITED_STRING(strOtherAccount, 65536));
if (!ser_action.ForRead()) {
WriteOrderPos(nOrderPos, mapValue);
if (!(mapValue.empty() && _ssExtra.empty())) {
CDataStream ss(s.GetType(), s.GetVersion());
ss.insert(ss.begin(), '\0');
ss << mapValue;
ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end());
strComment.append(ss.str());
}
}
READWRITE(LIMITED_STRING(strComment, 65536));
size_t nSepPos = strComment.find("\0", 0, 1);
if (ser_action.ForRead()) {
mapValue.clear();
if (std::string::npos != nSepPos) {
CDataStream ss(
std::vector<char>(strComment.begin() + nSepPos + 1,
strComment.end()),
s.GetType(), s.GetVersion());
ss >> mapValue;
_ssExtra = std::vector<char>(ss.begin(), ss.end());
}
ReadOrderPos(nOrderPos, mapValue);
}
if (std::string::npos != nSepPos) strComment.erase(nSepPos);
mapValue.erase("n");
}
private:
std::vector<char> _ssExtra;
};
/**
* A CWallet is an extension of a keystore, which also maintains a set of
* transactions and balances, and provides the ability to create new
* transactions.
*/
class CWallet final : public CCryptoKeyStore, public CValidationInterface {
private:
static std::atomic<bool> fFlushScheduled;
std::atomic<bool> fAbortRescan;
std::atomic<bool> fScanningWallet;
/**
* Select a set of coins such that nValueRet >= nTargetValue and at least
* all coins from coinControl are selected; Never select unconfirmed coins
* if they are not ours.
*/
bool SelectCoins(
const std::vector<COutput> &vAvailableCoins, const Amount nTargetValue,
std::set<std::pair<const CWalletTx *, unsigned int>> &setCoinsRet,
Amount &nValueRet, const CCoinControl *coinControl = nullptr) const;
CWalletDB *pwalletdbEncryption;
//! the current wallet version: clients below this version are not able to
//! load the wallet
int nWalletVersion;
//! the maximum wallet format version: memory-only variable that specifies
//! to what version this wallet may be upgraded
int nWalletMaxVersion;
int64_t nNextResend;
int64_t nLastResend;
bool fBroadcastTransactions;
/**
* Used to keep track of spent outpoints, and detect and report conflicts
* (double-spends or mutated transactions where the mutant gets mined).
*/
typedef std::multimap<COutPoint, TxId> TxSpends;
TxSpends mapTxSpends;
void AddToSpends(const COutPoint &outpoint, const TxId &wtxid);
void AddToSpends(const TxId &wtxid);
/**
* Mark a transaction (and its in-wallet descendants) as conflicting with a
* particular block.
*/
void MarkConflicted(const uint256 &hashBlock, const TxId &txid);
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>);
/**
* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected.
* Should be called with pindexBlock and posInBlock if this is for a
* transaction that is included in a block.
*/
void SyncTransaction(const CTransactionRef &tx,
const CBlockIndex *pindex = nullptr,
int posInBlock = 0);
/* the HD chain data model (external chain counters) */
CHDChain hdChain;
/* HD derive new child key (on internal or external chain) */
void DeriveNewChildKey(CWalletDB &walletdb, CKeyMetadata &metadata,
CKey &secret, bool internal = false);
std::set<int64_t> setInternalKeyPool;
std::set<int64_t> setExternalKeyPool;
int64_t m_max_keypool_index;
std::map<CKeyID, int64_t> m_pool_key_to_index;
int64_t nTimeFirstKey;
/**
* Private version of AddWatchOnly method which does not accept a timestamp,
* and which will reset the wallet's nTimeFirstKey value to 1 if the watch
* key did not previously have a timestamp associated with it. Because this
* is an inherited virtual method, it is accessible despite being marked
* private, but it is marked private anyway to encourage use of the other
* AddWatchOnly which accepts a timestamp and sets nTimeFirstKey more
* intelligently for more efficient rescans.
*/
bool AddWatchOnly(const CScript &dest) override;
std::unique_ptr<CWalletDBWrapper> dbw;
public:
const CChainParams &chainParams;
/*
* Main wallet lock.
* This lock protects all the fields added by CWallet.
*/
mutable CCriticalSection cs_wallet;
/**
* Get database handle used by this wallet. Ideally this function would not
* be necessary.
*/
CWalletDBWrapper &GetDBHandle() { return *dbw; }
/**
* Get a name for this wallet for logging/debugging purposes.
*/
std::string GetName() const {
if (dbw) {
return dbw->GetName();
} else {
return "dummy";
}
}
void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool);
// Map from Key ID (for regular keys) or Script ID (for watch-only keys) to
// key metadata.
std::map<CTxDestination, CKeyMetadata> mapKeyMetadata;
typedef std::map<unsigned int, CMasterKey> MasterKeyMap;
MasterKeyMap mapMasterKeys;
unsigned int nMasterKeyMaxID;
// Create wallet with dummy database handle
CWallet(const CChainParams &chainParamsIn)
: dbw(new CWalletDBWrapper()), chainParams(chainParamsIn) {
SetNull();
}
// Create wallet with passed-in database handle
CWallet(const CChainParams &chainParamsIn,
std::unique_ptr<CWalletDBWrapper> dbw_in)
: dbw(std::move(dbw_in)), chainParams(chainParamsIn) {
SetNull();
}
~CWallet() {
delete pwalletdbEncryption;
pwalletdbEncryption = nullptr;
}
void SetNull() {
nWalletVersion = FEATURE_BASE;
nWalletMaxVersion = FEATURE_BASE;
nMasterKeyMaxID = 0;
pwalletdbEncryption = nullptr;
nOrderPosNext = 0;
nAccountingEntryNumber = 0;
nNextResend = 0;
nLastResend = 0;
m_max_keypool_index = 0;
nTimeFirstKey = 0;
fBroadcastTransactions = false;
fAbortRescan = false;
fScanningWallet = false;
}
std::map<TxId, CWalletTx> mapWallet;
std::list<CAccountingEntry> laccentries;
typedef std::pair<CWalletTx *, CAccountingEntry *> TxPair;
typedef std::multimap<int64_t, TxPair> TxItems;
TxItems wtxOrdered;
int64_t nOrderPosNext;
uint64_t nAccountingEntryNumber;
std::map<uint256, int> mapRequestCount;
std::map<CTxDestination, CAddressBookData> mapAddressBook;
std::set<COutPoint> setLockedCoins;
const CWalletTx *GetWalletTx(const TxId &txid) const;
//! check whether we are allowed to upgrade (or already support) to the
//! named feature
bool CanSupportFeature(enum WalletFeature wf) const {
AssertLockHeld(cs_wallet);
return nWalletMaxVersion >= wf;
}
/**
* populate vCoins with vector of available COutputs.
*/
void AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe = true,
const CCoinControl *coinControl = nullptr,
const Amount nMinimumAmount = SATOSHI,
const Amount nMaximumAmount = MAX_MONEY,
const Amount nMinimumSumAmount = MAX_MONEY,
const uint64_t &nMaximumCount = 0,
const int &nMinDepth = 0,
const int &nMaxDepth = 9999999) const;
/**
* Shuffle and select coins until nTargetValue is reached while avoiding
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled.
*/
bool SelectCoinsMinConf(
const Amount nTargetValue, int nConfMine, int nConfTheirs,
uint64_t nMaxAncestors, std::vector<COutput> vCoins,
std::set<std::pair<const CWalletTx *, unsigned int>> &setCoinsRet,
Amount &nValueRet) const;
bool IsSpent(const TxId &txid, uint32_t n) const;
bool IsLockedCoin(const TxId &txid, uint32_t n) const;
void LockCoin(const COutPoint &output);
void UnlockCoin(const COutPoint &output);
void UnlockAllCoins();
void ListLockedCoins(std::vector<COutPoint> &vOutpts);
/*
* Rescan abort properties
*/
void AbortRescan() { fAbortRescan = true; }
bool IsAbortingRescan() { return fAbortRescan; }
bool IsScanning() { return fScanningWallet; }
/**
* keystore implementation
* Generate a new key
*/
CPubKey GenerateNewKey(CWalletDB &walletdb, bool internal = false);
//! Adds a key to the store, and saves it to disk.
bool AddKeyPubKey(const CKey &key, const CPubKey &pubkey) override;
bool AddKeyPubKeyWithDB(CWalletDB &walletdb, const CKey &key,
const CPubKey &pubkey);
//! Adds a key to the store, without saving it to disk (used by LoadWallet)
bool LoadKey(const CKey &key, const CPubKey &pubkey) {
return CCryptoKeyStore::AddKeyPubKey(key, pubkey);
}
//! Load metadata (used by LoadWallet)
bool LoadKeyMetadata(const CTxDestination &pubKey,
const CKeyMetadata &metadata);
bool LoadMinVersion(int nVersion) {
AssertLockHeld(cs_wallet);
nWalletVersion = nVersion;
nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion);
return true;
}
void UpdateTimeFirstKey(int64_t nCreateTime);
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret) override;
//! Adds an encrypted key to the store, without saving it to disk (used by
//! LoadWallet)
bool LoadCryptedKey(const CPubKey &vchPubKey,
const std::vector<uint8_t> &vchCryptedSecret);
bool AddCScript(const CScript &redeemScript) override;
bool LoadCScript(const CScript &redeemScript);
//! Adds a destination data tuple to the store, and saves it to disk
bool AddDestData(const CTxDestination &dest, const std::string &key,
const std::string &value);
//! Erases a destination data tuple in the store and on disk
bool EraseDestData(const CTxDestination &dest, const std::string &key);
//! Adds a destination data tuple to the store, without saving it to disk
bool LoadDestData(const CTxDestination &dest, const std::string &key,
const std::string &value);
//! Look up a destination data tuple in the store, return true if found
//! false otherwise
bool GetDestData(const CTxDestination &dest, const std::string &key,
std::string *value) const;
//! Adds a watch-only address to the store, and saves it to disk.
bool AddWatchOnly(const CScript &dest, int64_t nCreateTime);
bool RemoveWatchOnly(const CScript &dest) override;
//! Adds a watch-only address to the store, without saving it to disk (used
//! by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
//! Holds a timestamp at which point the wallet is scheduled (externally) to
//! be relocked. Caller must arrange for actual relocking to occur via
//! Lock().
int64_t nRelockTime;
bool Unlock(const SecureString &strWalletPassphrase);
bool ChangeWalletPassphrase(const SecureString &strOldWalletPassphrase,
const SecureString &strNewWalletPassphrase);
bool EncryptWallet(const SecureString &strWalletPassphrase);
void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const;
unsigned int ComputeTimeSmart(const CWalletTx &wtx) const;
/**
* Increment the next transaction order id
* @return next transaction order id
*/
int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr);
DBErrors ReorderTransactions();
bool AccountMove(std::string strFrom, std::string strTo,
const Amount nAmount, std::string strComment = "");
bool GetAccountPubkey(CPubKey &pubKey, std::string strAccount,
bool bForceNew = false);
void MarkDirty();
bool AddToWallet(const CWalletTx &wtxIn, bool fFlushOnClose = true);
bool LoadToWallet(const CWalletTx &wtxIn);
void TransactionAddedToMempool(const CTransactionRef &tx) override;
void
BlockConnected(const std::shared_ptr<const CBlock> &pblock,
const CBlockIndex *pindex,
const std::vector<CTransactionRef> &vtxConflicted) override;
void
BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) override;
bool AddToWalletIfInvolvingMe(const CTransactionRef &tx,
const CBlockIndex *pIndex, int posInBlock,
bool fUpdate);
int64_t RescanFromTime(int64_t startTime, bool update);
CBlockIndex *ScanForWalletTransactions(CBlockIndex *pindexStart,
CBlockIndex *pindexStop,
bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime,
CConnman *connman) override;
// ResendWalletTransactionsBefore may only be called if
// fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(int64_t nTime,
CConnman *connman);
Amount GetBalance() const;
Amount GetUnconfirmedBalance() const;
Amount GetImmatureBalance() const;
Amount GetWatchOnlyBalance() const;
Amount GetUnconfirmedWatchOnlyBalance() const;
Amount GetImmatureWatchOnlyBalance() const;
Amount GetLegacyBalance(const isminefilter &filter, int minDepth,
const std::string *account) const;
/**
* Insert additional inputs into the transaction by calling
* CreateTransaction();
*/
bool FundTransaction(CMutableTransaction &tx, Amount &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
* selected by SelectCoins(); Also create the change output, when needed
* @note passing nChangePosInOut as -1 will result in setting a random
* position
*/
bool CreateTransaction(const std::vector<CRecipient> &vecSend,
CWalletTx &wtxNew, CReserveKey &reservekey,
Amount &nFeeRet, int &nChangePosInOut,
std::string &strFailReason,
const CCoinControl *coinControl = nullptr,
bool sign = true);
bool CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey,
CConnman *connman, CValidationState &state);
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 fallbackFee;
bool NewKeyPool();
size_t KeypoolCountExternalKeys();
bool TopUpKeyPool(unsigned int kpSize = 0);
void ReserveKeyFromKeyPool(int64_t &nIndex, CKeyPool &keypool,
bool fRequestedInternal);
void KeepKey(int64_t nIndex);
void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey &pubkey);
bool GetKeyFromPool(CPubKey &key, bool internal = false);
int64_t GetOldestKeyPoolTime();
/**
* Marks all keys in the keypool up to and including reserve_key as used.
*/
void MarkReserveKeysAsUsed(int64_t keypool_id);
const std::map<CKeyID, int64_t> &GetAllReserveKeys() const {
return m_pool_key_to_index;
}
/** Does the wallet have at least min_keys in the keypool? */
bool HasUnusedKeys(size_t min_keys) const;
std::set<std::set<CTxDestination>> GetAddressGroupings();
std::map<CTxDestination, Amount> GetAddressBalances();
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
*/
Amount GetDebit(const CTxIn &txin, const isminefilter &filter) const;
isminetype IsMine(const CTxOut &txout) const;
Amount GetCredit(const CTxOut &txout, const isminefilter &filter) const;
bool IsChange(const CTxOut &txout) const;
Amount GetChange(const CTxOut &txout) const;
bool IsMine(const CTransaction &tx) const;
/** should probably be renamed to IsRelevantToMe */
bool IsFromMe(const CTransaction &tx) const;
Amount 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;
Amount GetCredit(const CTransaction &tx, const isminefilter &filter) const;
Amount GetChange(const CTransaction &tx) const;
void SetBestChain(const CBlockLocator &loc) override;
DBErrors LoadWallet(bool &fFirstRunRet);
DBErrors ZapWalletTx(std::vector<CWalletTx> &vWtx);
DBErrors ZapSelectTx(std::vector<TxId> &txIdsIn,
std::vector<TxId> &txIdsOut);
bool SetAddressBook(const CTxDestination &address,
const std::string &strName, const std::string &purpose);
bool DelAddressBook(const CTxDestination &address);
const std::string &GetAccountName(const CScript &scriptPubKey) const;
void Inventory(const uint256 &hash) override {
LOCK(cs_wallet);
std::map<uint256, int>::iterator mi = mapRequestCount.find(hash);
if (mi != mapRequestCount.end()) {
(*mi).second++;
}
}
void GetScriptForMining(std::shared_ptr<CReserveScript> &script);
unsigned int GetKeyPoolSize() {
// set{Ex,In}ternalKeyPool
AssertLockHeld(cs_wallet);
return setInternalKeyPool.size() + setExternalKeyPool.size();
}
//! signify that a particular wallet feature is now used. this may change
//! nWalletVersion and nWalletMaxVersion if those are lower
bool SetMinVersion(enum WalletFeature, CWalletDB *pwalletdbIn = nullptr,
bool fExplicit = false);
//! change which version we're allowed to upgrade to (note that this does
//! not immediately imply upgrading to that format)
bool SetMaxVersion(int nVersion);
//! get the current wallet format (the oldest client version guaranteed to
//! understand this wallet)
int GetVersion() {
LOCK(cs_wallet);
return nWalletVersion;
}
//! Get wallet transactions that conflict with given transaction (spend same
//! outputs)
std::set<TxId> GetConflicts(const TxId &txid) const;
//! Check if a given transaction has any of its outputs spent by another
//! transaction in the wallet
bool HasWalletSpend(const TxId &txid) const;
//! Flush wallet (bitdb flush)
void Flush(bool shutdown = false);
/**
* Address book entry changed.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void(CWallet *wallet, const CTxDestination &address,
const std::string &label, bool isMine,
const std::string &purpose, ChangeType status)>
NotifyAddressBookChanged;
/**
* Wallet transaction added, removed or updated.
* @note called with lock cs_wallet held.
*/
boost::signals2::signal<void(CWallet *wallet, const TxId &txid,
ChangeType status)>
NotifyTransactionChanged;
/** Show progress e.g. for rescan */
boost::signals2::signal<void(const std::string &title, int nProgress)>
ShowProgress;
/** Watch-only address added */
boost::signals2::signal<void(bool fHaveWatchOnly)> NotifyWatchonlyChanged;
/** Inquire whether this wallet broadcasts transactions. */
bool GetBroadcastTransactions() const { return fBroadcastTransactions; }
/** Set whether this wallet broadcasts transactions. */
void SetBroadcastTransactions(bool broadcast) {
fBroadcastTransactions = broadcast;
}
/**
* Mark a transaction (and it in-wallet descendants) as abandoned so its
* inputs may be respent.
*/
bool AbandonTransaction(const TxId &txid);
/**
* Initializes the wallet, returns a new CWallet instance or a null pointer
* in case of an error.
*/
static CWallet *CreateWalletFromFile(const CChainParams &chainParams,
const std::string walletFile);
/**
* Wallet post-init setup
* Gives the wallet a chance to register repetitive tasks and complete
* post-init tasks
*/
void postInitProcess(CScheduler &scheduler);
bool BackupWallet(const std::string &strDest);
/* Set the HD chain model (chain child index counters) */
bool SetHDChain(const CHDChain &chain, bool memonly);
const CHDChain &GetHDChain() { return hdChain; }
/* Returns true if HD is enabled */
bool IsHDEnabled();
/* Generates a new HD master key (will not be activated) */
CPubKey GenerateNewHDMasterKey();
/**
* Set the current HD master key (will reset the chain child index counters)
* If possibleOldChain is provided, the parameters from the old chain
* (version) will be preserved.
*/
bool SetHDMasterKey(const CPubKey &key,
CHDChain *possibleOldChain = nullptr);
};
/** A key allocated from the key pool. */
class CReserveKey final : public CReserveScript {
protected:
CWallet *pwallet;
int64_t nIndex;
CPubKey vchPubKey;
bool fInternal;
public:
CReserveKey(CWallet *pwalletIn) {
nIndex = -1;
pwallet = pwalletIn;
fInternal = false;
}
~CReserveKey() { ReturnKey(); }
void ReturnKey();
bool GetReservedKey(CPubKey &pubkey, bool internal = false);
void KeepKey();
void KeepScript() override { KeepKey(); }
};
/**
* Account information.
* Stored in wallet with key "acc"+string account name.
*/
class CAccount {
public:
CPubKey vchPubKey;
CAccount() { SetNull(); }
void SetNull() { vchPubKey = CPubKey(); }
ADD_SERIALIZE_METHODS;
template <typename Stream, typename Operation>
inline void SerializationOp(Stream &s, Operation ser_action) {
int nVersion = s.GetVersion();
if (!(s.GetType() & SER_GETHASH)) {
READWRITE(nVersion);
}
READWRITE(vchPubKey);
}
};
// 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

File Metadata

Mime Type
text/x-diff
Expires
Wed, May 21, 21:19 (1 d, 7 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865941
Default Alt Text
(341 KB)

Event Timeline