aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.h')
-rw-r--r--src/wallet/wallet.h295
1 files changed, 140 insertions, 155 deletions
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3e2d1794d8..3ef5bfbb65 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -17,6 +17,7 @@
#include <script/sign.h>
#include <util.h>
#include <wallet/crypter.h>
+#include <wallet/coinselection.h>
#include <wallet/walletdb.h>
#include <wallet/rpcwallet.h>
@@ -42,6 +43,7 @@ extern bool bSpendZeroConfChange;
extern bool fWalletRbf;
extern bool g_wallet_allow_fallback_fee;
+//! Default for -keypool
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
@@ -53,10 +55,6 @@ static const CAmount DEFAULT_DISCARD_FEE = 10000;
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
//! minimum recommended increment for BIP 125 replacement txs
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
-//! target minimum change amount
-static const CAmount MIN_CHANGE = CENT;
-//! final minimum change amount after paying for fees
-static const CAmount MIN_FINAL_CHANGE = MIN_CHANGE/2;
//! Default for -spendzeroconfchange
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -walletrejectlongchains
@@ -99,18 +97,15 @@ enum WalletFeature
FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version
};
-enum OutputType : int
-{
- OUTPUT_TYPE_NONE,
- OUTPUT_TYPE_LEGACY,
- OUTPUT_TYPE_P2SH_SEGWIT,
- OUTPUT_TYPE_BECH32,
-
- OUTPUT_TYPE_DEFAULT = OUTPUT_TYPE_P2SH_SEGWIT
+enum class OutputType {
+ NONE,
+ LEGACY,
+ P2SH_SEGWIT,
+ BECH32,
};
-extern OutputType g_address_type;
-extern OutputType g_change_type;
+//! Default for -addresstype
+constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
/** A key pool entry */
@@ -269,6 +264,9 @@ public:
bool IsCoinBase() const { return tx->IsCoinBase(); }
};
+//Get the marginal bytes of spending the specified output
+int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet);
+
/**
* 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.
@@ -348,11 +346,6 @@ public:
mutable CAmount nAvailableWatchCreditCached;
mutable CAmount nChangeCached;
- CWalletTx()
- {
- Init(nullptr);
- }
-
CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) : CMerkleTx(std::move(arg))
{
Init(pwalletIn);
@@ -390,42 +383,36 @@ public:
nOrderPos = -1;
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- if (ser_action.ForRead())
- Init(nullptr);
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
char fSpent = false;
+ mapValue_t mapValueCopy = mapValue;
- if (!ser_action.ForRead())
- {
- mapValue["fromaccount"] = strFromAccount;
-
- WriteOrderPos(nOrderPos, mapValue);
-
- if (nTimeSmart)
- mapValue["timesmart"] = strprintf("%u", nTimeSmart);
+ mapValueCopy["fromaccount"] = strFromAccount;
+ WriteOrderPos(nOrderPos, mapValueCopy);
+ if (nTimeSmart) {
+ mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
}
- READWRITE(*static_cast<CMerkleTx*>(this));
+ s << *static_cast<const CMerkleTx*>(this);
std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev
- READWRITE(vUnused);
- READWRITE(mapValue);
- READWRITE(vOrderForm);
- READWRITE(fTimeReceivedIsTxTime);
- READWRITE(nTimeReceived);
- READWRITE(fFromMe);
- READWRITE(fSpent);
-
- if (ser_action.ForRead())
- {
- strFromAccount = mapValue["fromaccount"];
+ s << vUnused << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << fSpent;
+ }
+
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
+ Init(nullptr);
+ char fSpent;
- ReadOrderPos(nOrderPos, mapValue);
+ s >> *static_cast<CMerkleTx*>(this);
+ std::vector<CMerkleTx> vUnused; //!< Used to be vtxPrev
+ s >> vUnused >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> fSpent;
- nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
- }
+ strFromAccount = std::move(mapValue["fromaccount"]);
+ ReadOrderPos(nOrderPos, mapValue);
+ nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
mapValue.erase("fromaccount");
mapValue.erase("spent");
@@ -462,6 +449,12 @@ public:
CAmount GetAvailableWatchOnlyCredit(const bool fUseCache=true) const;
CAmount GetChange() const;
+ // Get the marginal bytes if spending the specified output from this transaction
+ int GetSpendSize(unsigned int out) const
+ {
+ return CalculateMaximumSignedInputSize(tx->vout[out], pwallet);
+ }
+
void GetAmounts(std::list<COutputEntry>& listReceived,
std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const;
@@ -488,36 +481,6 @@ public:
std::set<uint256> GetConflicts() const;
};
-
-class CInputCoin {
-public:
- CInputCoin(const CWalletTx* walletTx, unsigned int i)
- {
- if (!walletTx)
- throw std::invalid_argument("walletTx should not be null");
- if (i >= walletTx->tx->vout.size())
- throw std::out_of_range("The output index is out of range");
-
- outpoint = COutPoint(walletTx->GetHash(), i);
- txout = walletTx->tx->vout[i];
- }
-
- COutPoint outpoint;
- CTxOut txout;
-
- bool operator<(const CInputCoin& rhs) const {
- return outpoint < rhs.outpoint;
- }
-
- bool operator!=(const CInputCoin& rhs) const {
- return outpoint != rhs.outpoint;
- }
-
- bool operator==(const CInputCoin& rhs) const {
- return outpoint == rhs.outpoint;
- }
-};
-
class COutput
{
public:
@@ -525,6 +488,9 @@ public:
int i;
int nDepth;
+ /** Pre-computed estimated size of this output as a fully-signed input in a transaction. Can be -1 if it could not be calculated */
+ int nInputBytes;
+
/** Whether we have the private keys to spend this output */
bool fSpendable;
@@ -540,7 +506,12 @@ public:
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;
+ tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1;
+ // If known and signable by the given wallet, compute nInputBytes
+ // Failure will keep this value -1
+ if (fSpendable && tx) {
+ nInputBytes = tx->GetSpendSize(i);
+ }
}
std::string ToString() const;
@@ -608,48 +579,49 @@ public:
nEntryNo = 0;
}
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
+ template <typename Stream>
+ void Serialize(Stream& s) const {
int nVersion = s.GetVersion();
- if (!(s.GetType() & SER_GETHASH))
- READWRITE(nVersion);
+ if (!(s.GetType() & SER_GETHASH)) {
+ s << 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());
- }
+ s << nCreditDebit << nTime << strOtherAccount;
+
+ mapValue_t mapValueCopy = mapValue;
+ WriteOrderPos(nOrderPos, mapValueCopy);
+
+ std::string strCommentCopy = strComment;
+ if (!mapValueCopy.empty() || !_ssExtra.empty()) {
+ CDataStream ss(s.GetType(), s.GetVersion());
+ ss.insert(ss.begin(), '\0');
+ ss << mapValueCopy;
+ ss.insert(ss.end(), _ssExtra.begin(), _ssExtra.end());
+ strCommentCopy.append(ss.str());
}
+ s << strCommentCopy;
+ }
- READWRITE(LIMITED_STRING(strComment, 65536));
+ template <typename Stream>
+ void Unserialize(Stream& s) {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH)) {
+ s >> nVersion;
+ }
+ //! Note: strAccount is serialized as part of the key, not here.
+ s >> nCreditDebit >> nTime >> LIMITED_STRING(strOtherAccount, 65536) >> 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);
+ 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());
}
- if (std::string::npos != nSepPos)
+ ReadOrderPos(nOrderPos, mapValue);
+ if (std::string::npos != nSepPos) {
strComment.erase(nSepPos);
+ }
mapValue.erase("n");
}
@@ -658,6 +630,26 @@ private:
std::vector<char> _ssExtra;
};
+struct CoinSelectionParams
+{
+ bool use_bnb = true;
+ size_t change_output_size = 0;
+ size_t change_spend_size = 0;
+ CFeeRate effective_fee = CFeeRate(0);
+ size_t tx_noinputs_size = 0;
+
+ CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {}
+ CoinSelectionParams() {}
+};
+
+struct CoinEligibilityFilter
+{
+ const int conf_mine;
+ const int conf_theirs;
+ const uint64_t max_ancestors;
+
+ CoinEligibilityFilter(int conf_mine, int conf_theirs, uint64_t max_ancestors) : conf_mine(conf_mine), conf_theirs(conf_theirs), max_ancestors(max_ancestors) {}
+};
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
/**
@@ -673,14 +665,6 @@ private:
std::mutex mutexScanning;
friend class WalletRescanReserver;
-
- /**
- * 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 CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = nullptr) const;
-
CWalletDB *pwalletdbEncryption;
//! the current wallet version: clients below this version are not able to load the wallet
@@ -773,6 +757,14 @@ public:
return *dbw;
}
+ /**
+ * 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 CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet,
+ const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
+
/** Get a name for this wallet for logging/debugging purposes.
*/
const std::string& GetName() const { return m_name; }
@@ -860,7 +852,8 @@ public:
* completion the coin set and corresponding actual target value is
* assembled
*/
- bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, uint64_t nMaxAncestors, std::vector<COutput> vCoins, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet) const;
+ bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> vCoins,
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -935,7 +928,7 @@ public:
int64_t IncOrderPosNext(CWalletDB *pwalletdb = nullptr);
DBErrors ReorderTransactions();
bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "");
- bool GetAccountDestination(CTxDestination &dest, std::string strAccount, bool bForceNew = false);
+ bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false);
void MarkDirty();
bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true);
@@ -974,19 +967,27 @@ public:
* 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, CAmount& nFeeRet, int& nChangePosInOut,
+ bool CreateTransaction(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true);
- bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state);
+ bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::string fromAccount, 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) const;
+ bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts) const
+ {
+ std::vector<CTxOut> v_txouts(txouts.size());
+ std::copy(txouts.begin(), txouts.end(), v_txouts.begin());
+ return DummySignTx(txNew, v_txouts);
+ }
+ bool DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts) const;
+ bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const;
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
static CFeeRate m_discard_rate;
+ OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
+ OutputType m_default_change_type{OutputType::NONE}; // Default to OutputType::NONE if not set by -changetype
bool NewKeyPool();
size_t KeypoolCountExternalKeys();
@@ -1005,7 +1006,7 @@ public:
std::set< std::set<CTxDestination> > GetAddressGroupings();
std::map<CTxDestination, CAmount> GetAddressBalances();
- std::set<CTxDestination> GetAccountAddresses(const std::string& strAccount) const;
+ std::set<CTxDestination> GetLabelAddresses(const std::string& label) const;
isminetype IsMine(const CTxIn& txin) const;
/**
@@ -1035,7 +1036,7 @@ public:
bool DelAddressBook(const CTxDestination& address);
- const std::string& GetAccountName(const CScript& scriptPubKey) const;
+ const std::string& GetLabelName(const CScript& scriptPubKey) const;
void Inventory(const uint256 &hash) override
{
@@ -1163,6 +1164,9 @@ public:
* This function will automatically add the necessary scripts to the wallet.
*/
CTxDestination AddAndGetDestinationForScript(const CScript& script, OutputType);
+
+ /** Whether a given output is spendable by this wallet */
+ bool OutputEligibleForSpending(const COutput& output, const CoinEligibilityFilter& eligibility_filter) const;
};
/** A key allocated from the key pool. */
@@ -1227,32 +1231,7 @@ public:
}
};
-// Helper for producing a bunch of max-sized low-S signatures (eg 72 bytes)
-// ContainerType is meant to hold pair<CWalletTx *, int>, and be iterable
-// so that each entry corresponds to each vIn, in order.
-template <typename ContainerType>
-bool CWallet::DummySignTx(CMutableTransaction &txNew, const ContainerType &coins) const
-{
- // Fill in dummy signatures for fee calculation.
- int nIn = 0;
- for (const auto& coin : coins)
- {
- const CScript& scriptPubKey = coin.txout.scriptPubKey;
- SignatureData sigdata;
-
- if (!ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata))
- {
- return false;
- } else {
- UpdateTransaction(txNew, nIn, sigdata);
- }
-
- nIn++;
- }
- return true;
-}
-
-OutputType ParseOutputType(const std::string& str, OutputType default_type = OUTPUT_TYPE_DEFAULT);
+OutputType ParseOutputType(const std::string& str, OutputType default_type);
const std::string& FormatOutputType(OutputType type);
/**
@@ -1299,4 +1278,10 @@ public:
}
};
+// Calculate the size of the transaction assuming all signatures are max size
+// Use DummySignatureCreator, which inserts 72 byte signatures everywhere.
+// NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+// be IsAllFromMe).
+int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet);
+int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts);
#endif // BITCOIN_WALLET_WALLET_H