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.h520
1 files changed, 77 insertions, 443 deletions
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 4fc4466604..4a3e0f7054 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -21,7 +21,11 @@
#include <validationinterface.h>
#include <wallet/coinselection.h>
#include <wallet/crypter.h>
+#include <external_signer.h>
+#include <wallet/receive.h>
#include <wallet/scriptpubkeyman.h>
+#include <wallet/spend.h>
+#include <wallet/transaction.h>
#include <wallet/walletdb.h>
#include <wallet/walletutil.h>
@@ -29,6 +33,7 @@
#include <atomic>
#include <map>
#include <memory>
+#include <optional>
#include <set>
#include <stdexcept>
#include <stdint.h>
@@ -50,12 +55,12 @@ struct bilingual_str;
void UnloadWallet(std::shared_ptr<CWallet>&& wallet);
bool AddWallet(const std::shared_ptr<CWallet>& wallet);
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings);
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings);
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start);
std::vector<std::shared_ptr<CWallet>> GetWallets();
std::shared_ptr<CWallet> GetWallet(const std::string& name);
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
-std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings);
std::unique_ptr<interfaces::Handler> HandleLoadWallet(LoadWalletFn load_wallet);
std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error);
@@ -95,7 +100,6 @@ constexpr CAmount DEFAULT_TRANSACTION_MAXFEE{COIN / 10};
constexpr CAmount HIGH_TX_FEE_PER_KB{COIN / 100};
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
constexpr CAmount HIGH_MAX_TX_FEE{100 * HIGH_TX_FEE_PER_KB};
-
//! Pre-calculated constants for input size estimation in *virtual size*
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;
@@ -115,7 +119,8 @@ static constexpr uint64_t KNOWN_WALLET_FLAGS =
| WALLET_FLAG_BLANK_WALLET
| WALLET_FLAG_KEY_ORIGIN_METADATA
| WALLET_FLAG_DISABLE_PRIVATE_KEYS
- | WALLET_FLAG_DESCRIPTORS;
+ | WALLET_FLAG_DESCRIPTORS
+ | WALLET_FLAG_EXTERNAL_SIGNER;
static constexpr uint64_t MUTABLE_WALLET_FLAGS =
WALLET_FLAG_AVOID_REUSE;
@@ -126,6 +131,7 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
{"key_origin_metadata", WALLET_FLAG_KEY_ORIGIN_METADATA},
{"disable_private_keys", WALLET_FLAG_DISABLE_PRIVATE_KEYS},
{"descriptor_wallet", WALLET_FLAG_DESCRIPTORS},
+ {"external_signer", WALLET_FLAG_EXTERNAL_SIGNER}
};
extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
@@ -212,417 +218,6 @@ struct CRecipient
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"))
- {
- nOrderPos = -1; // TODO: calculate elsewhere
- return;
- }
- nOrderPos = atoi64(mapValue["n"]);
-}
-
-
-static inline void WriteOrderPos(const int64_t& nOrderPos, mapValue_t& mapValue)
-{
- if (nOrderPos == -1)
- return;
- mapValue["n"] = ToString(nOrderPos);
-}
-
-struct COutputEntry
-{
- CTxDestination destination;
- CAmount amount;
- int vout;
-};
-
-/** Legacy class used for deserializing vtxPrev for backwards compatibility.
- * vtxPrev was removed in commit 93a18a3650292afbb441a47d1fa1b94aeb0164e3,
- * but old wallet.dat files may still contain vtxPrev vectors of CMerkleTxs.
- * These need to get deserialized for field alignment when deserializing
- * a CWalletTx, but the deserialized values are discarded.**/
-class CMerkleTx
-{
-public:
- template<typename Stream>
- void Unserialize(Stream& s)
- {
- CTransactionRef tx;
- uint256 hashBlock;
- std::vector<uint256> vMerkleBranch;
- int nIndex;
-
- s >> tx >> hashBlock >> vMerkleBranch >> nIndex;
- }
-};
-
-//Get the marginal bytes of spending the specified output
-int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet, bool use_max_sig = false);
-
-/**
- * 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
-{
-private:
- const CWallet* const pwallet;
-
- /** Constant used in hashBlock to indicate tx has been abandoned, only used at
- * serialization/deserialization to avoid ambiguity with conflicted.
- */
- static constexpr const uint256& ABANDON_HASH = uint256::ONE;
-
-public:
- /**
- * Key/value map with information about the transaction.
- *
- * The following keys can be read and written through the map and are
- * serialized in the wallet database:
- *
- * "comment", "to" - comment strings provided to sendtoaddress,
- * and sendmany wallet RPCs
- * "replaces_txid" - txid (as HexStr) of transaction replaced by
- * bumpfee on transaction created by bumpfee
- * "replaced_by_txid" - txid (as HexStr) of transaction created by
- * bumpfee on transaction replaced by bumpfee
- * "from", "message" - obsolete fields that could be set in UI prior to
- * 2011 (removed in commit 4d9b223)
- *
- * The following keys are serialized in the wallet database, but shouldn't
- * be read or written through the map (they will be temporarily added and
- * removed from the map during serialization):
- *
- * "fromaccount" - serialized strFromAccount value
- * "n" - serialized nOrderPos value
- * "timesmart" - serialized nTimeSmart value
- * "spent" - serialized vfSpent value that existed prior to
- * 2014 (removed in commit 93a18a3)
- */
- mapValue_t mapValue;
- std::vector<std::pair<std::string, std::string> > vOrderForm;
- unsigned int fTimeReceivedIsTxTime;
- unsigned int nTimeReceived; //!< time received by this node
- /**
- * 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.
- */
- bool fFromMe;
- int64_t nOrderPos; //!< position in ordered transaction list
- std::multimap<int64_t, CWalletTx*>::const_iterator m_it_wtxOrdered;
-
- // memory only
- enum AmountType { DEBIT, CREDIT, IMMATURE_CREDIT, AVAILABLE_CREDIT, AMOUNTTYPE_ENUM_ELEMENTS };
- CAmount GetCachableAmount(AmountType type, const isminefilter& filter, bool recalculate = false) const;
- mutable CachableAmount m_amounts[AMOUNTTYPE_ENUM_ELEMENTS];
- /**
- * This flag is true if all m_amounts caches are empty. This is particularly
- * useful in places where MarkDirty is conditionally called and the
- * condition can be expensive and thus can be skipped if the flag is true.
- * See MarkDestinationsDirty.
- */
- mutable bool m_is_cache_empty{true};
- mutable bool fChangeCached;
- mutable bool fInMempool;
- mutable CAmount nChangeCached;
-
- CWalletTx(const CWallet* wallet, CTransactionRef arg)
- : pwallet(wallet),
- tx(std::move(arg))
- {
- Init();
- }
-
- void Init()
- {
- mapValue.clear();
- vOrderForm.clear();
- fTimeReceivedIsTxTime = false;
- nTimeReceived = 0;
- nTimeSmart = 0;
- fFromMe = false;
- fChangeCached = false;
- fInMempool = false;
- nChangeCached = 0;
- nOrderPos = -1;
- m_confirm = Confirmation{};
- }
-
- CTransactionRef tx;
-
- /* New transactions start as UNCONFIRMED. At BlockConnected,
- * they will transition to CONFIRMED. In case of reorg, at BlockDisconnected,
- * they roll back to UNCONFIRMED. If we detect a conflicting transaction at
- * block connection, we update conflicted tx and its dependencies as CONFLICTED.
- * If tx isn't confirmed and outside of mempool, the user may switch it to ABANDONED
- * by using the abandontransaction call. This last status may be override by a CONFLICTED
- * or CONFIRMED transition.
- */
- enum Status {
- UNCONFIRMED,
- CONFIRMED,
- CONFLICTED,
- ABANDONED
- };
-
- /* Confirmation includes tx status and a triplet of {block height/block hash/tx index in block}
- * at which tx has been confirmed. All three are set to 0 if tx is unconfirmed or abandoned.
- * Meaning of these fields changes with CONFLICTED state where they instead point to block hash
- * and block height of the deepest conflicting tx.
- */
- struct Confirmation {
- Status status;
- int block_height;
- uint256 hashBlock;
- int nIndex;
- Confirmation(Status s = UNCONFIRMED, int b = 0, uint256 h = uint256(), int i = 0) : status(s), block_height(b), hashBlock(h), nIndex(i) {}
- };
-
- Confirmation m_confirm;
-
- template<typename Stream>
- void Serialize(Stream& s) const
- {
- mapValue_t mapValueCopy = mapValue;
-
- mapValueCopy["fromaccount"] = "";
- WriteOrderPos(nOrderPos, mapValueCopy);
- if (nTimeSmart) {
- mapValueCopy["timesmart"] = strprintf("%u", nTimeSmart);
- }
-
- std::vector<char> dummy_vector1; //!< Used to be vMerkleBranch
- std::vector<char> dummy_vector2; //!< Used to be vtxPrev
- bool dummy_bool = false; //!< Used to be fSpent
- uint256 serializedHash = isAbandoned() ? ABANDON_HASH : m_confirm.hashBlock;
- int serializedIndex = isAbandoned() || isConflicted() ? -1 : m_confirm.nIndex;
- s << tx << serializedHash << dummy_vector1 << serializedIndex << dummy_vector2 << mapValueCopy << vOrderForm << fTimeReceivedIsTxTime << nTimeReceived << fFromMe << dummy_bool;
- }
-
- template<typename Stream>
- void Unserialize(Stream& s)
- {
- Init();
-
- std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch
- std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev
- bool dummy_bool; //! Used to be fSpent
- int serializedIndex;
- s >> tx >> m_confirm.hashBlock >> dummy_vector1 >> serializedIndex >> dummy_vector2 >> mapValue >> vOrderForm >> fTimeReceivedIsTxTime >> nTimeReceived >> fFromMe >> dummy_bool;
-
- /* At serialization/deserialization, an nIndex == -1 means that hashBlock refers to
- * the earliest block in the chain we know this or any in-wallet ancestor conflicts
- * with. If nIndex == -1 and hashBlock is ABANDON_HASH, it means transaction is abandoned.
- * In same context, an nIndex >= 0 refers to a confirmed transaction (if hashBlock set) or
- * unconfirmed one. Older clients interpret nIndex == -1 as unconfirmed for backward
- * compatibility (pre-commit 9ac63d6).
- */
- if (serializedIndex == -1 && m_confirm.hashBlock == ABANDON_HASH) {
- setAbandoned();
- } else if (serializedIndex == -1) {
- setConflicted();
- } else if (!m_confirm.hashBlock.IsNull()) {
- m_confirm.nIndex = serializedIndex;
- setConfirmed();
- }
-
- ReadOrderPos(nOrderPos, mapValue);
- nTimeSmart = mapValue.count("timesmart") ? (unsigned int)atoi64(mapValue["timesmart"]) : 0;
-
- mapValue.erase("fromaccount");
- mapValue.erase("spent");
- mapValue.erase("n");
- mapValue.erase("timesmart");
- }
-
- void SetTx(CTransactionRef arg)
- {
- tx = std::move(arg);
- }
-
- //! make sure balances are recalculated
- void MarkDirty()
- {
- m_amounts[DEBIT].Reset();
- m_amounts[CREDIT].Reset();
- m_amounts[IMMATURE_CREDIT].Reset();
- m_amounts[AVAILABLE_CREDIT].Reset();
- fChangeCached = false;
- m_is_cache_empty = true;
- }
-
- //! filter decides which addresses will count towards the debit
- CAmount GetDebit(const isminefilter& filter) const;
- CAmount GetCredit(const isminefilter& filter) const;
- CAmount GetImmatureCredit(bool fUseCache = true) const;
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The
- // annotation "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid
- // having to resolve the issue of member access into incomplete type CWallet.
- CAmount GetAvailableCredit(bool fUseCache = true, const isminefilter& filter = ISMINE_SPENDABLE) const NO_THREAD_SAFETY_ANALYSIS;
- CAmount GetImmatureWatchOnlyCredit(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, bool use_max_sig = false) const
- {
- return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, use_max_sig);
- }
-
- void GetAmounts(std::list<COutputEntry>& listReceived,
- std::list<COutputEntry>& listSent, CAmount& nFee, const isminefilter& filter) const;
-
- bool IsFromMe(const isminefilter& filter) const
- {
- return (GetDebit(filter) > 0);
- }
-
- // True if only scriptSigs are different
- bool IsEquivalentTo(const CWalletTx& tx) const;
-
- bool InMempool() const;
- bool IsTrusted() const;
-
- int64_t GetTxTime() const;
-
- // Pass this transaction to node for mempool insertion and relay to peers if flag set to true
- bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
-
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- std::set<uint256> GetConflicts() const NO_THREAD_SAFETY_ANALYSIS;
-
- /**
- * 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
- */
- // TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
- // annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation
- // "NO_THREAD_SAFETY_ANALYSIS" was temporarily added to avoid having to
- // resolve the issue of member access into incomplete type CWallet. Note
- // that we still have the runtime check "AssertLockHeld(pwallet->cs_wallet)"
- // in place.
- int GetDepthInMainChain() const NO_THREAD_SAFETY_ANALYSIS;
- bool IsInMainChain() const { return GetDepthInMainChain() > 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;
- bool isAbandoned() const { return m_confirm.status == CWalletTx::ABANDONED; }
- void setAbandoned()
- {
- m_confirm.status = CWalletTx::ABANDONED;
- m_confirm.hashBlock = uint256();
- m_confirm.block_height = 0;
- m_confirm.nIndex = 0;
- }
- bool isConflicted() const { return m_confirm.status == CWalletTx::CONFLICTED; }
- void setConflicted() { m_confirm.status = CWalletTx::CONFLICTED; }
- bool isUnconfirmed() const { return m_confirm.status == CWalletTx::UNCONFIRMED; }
- void setUnconfirmed() { m_confirm.status = CWalletTx::UNCONFIRMED; }
- bool isConfirmed() const { return m_confirm.status == CWalletTx::CONFIRMED; }
- void setConfirmed() { m_confirm.status = CWalletTx::CONFIRMED; }
- const uint256& GetHash() const { return tx->GetHash(); }
- bool IsCoinBase() const { return tx->IsCoinBase(); }
- bool IsImmatureCoinBase() const;
-
- // Disable copying of CWalletTx objects to prevent bugs where instances get
- // copied in and out of the mapWallet map, and fields are updated in the
- // wrong copy.
- CWalletTx(CWalletTx const &) = delete;
- void operator=(CWalletTx const &x) = delete;
-};
-
-class COutput
-{
-public:
- const CWalletTx *tx;
- 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;
-
- /** Whether we know how to spend this output, ignoring the lack of keys */
- bool fSolvable;
-
- /** Whether to use the maximum sized, 72 byte signature when calculating the size of the input spend. This should only be set when watch-only outputs are allowed */
- bool use_max_sig;
-
- /**
- * Whether this output is considered safe to spend. Unconfirmed transactions
- * from outside keys and unconfirmed replacement transactions 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, bool use_max_sig_in = false)
- {
- tx = txIn; i = iIn; nDepth = nDepthIn; fSpendable = fSpendableIn; fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; use_max_sig = use_max_sig_in;
- // If known and signable by the given wallet, compute nInputBytes
- // Failure will keep this value -1
- if (fSpendable && tx) {
- nInputBytes = tx->GetSpendSize(i, use_max_sig);
- }
- }
-
- std::string ToString() const;
-
- inline CInputCoin GetInputCoin() const
- {
- return CInputCoin(tx->tx, i, nInputBytes);
- }
-};
-
-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;
- //! Indicate that we are subtracting the fee from outputs
- bool m_subtract_fee_outputs = false;
- bool m_avoid_partial_spends = false;
-
- CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size, bool avoid_partial) :
- 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),
- m_avoid_partial_spends(avoid_partial)
- {}
- CoinSelectionParams() {}
-};
-
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
/**
* A CWallet maintains a set of transactions and balances, and provides the ability to create new transactions.
@@ -632,7 +227,6 @@ class CWallet final : public WalletStorage, public interfaces::Chain::Notificati
private:
CKeyingMaterial vMasterKey GUARDED_BY(cs_wallet);
-
bool Unlock(const CKeyingMaterial& vMasterKeyIn, bool accept_no_keys = false);
std::atomic<bool> fAbortRescan{false};
@@ -644,7 +238,10 @@ private:
//! the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion GUARDED_BY(cs_wallet){FEATURE_BASE};
+ /** The next scheduled rebroadcast of wallet transactions. */
int64_t nNextResend = 0;
+ /** Whether this wallet will submit newly created transactions to the node's mempool and
+ * prompt rebroadcasts (see ResendWalletTransactions()). */
bool fBroadcastTransactions = false;
// Local time that the tip block was received. Used to schedule wallet rebroadcasts.
std::atomic<int64_t> m_best_block_time {0};
@@ -674,10 +271,10 @@ private:
*/
bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- /* Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
+ /** Mark a transaction (and its in-wallet descendants) as conflicting with a particular block. */
void MarkConflicted(const uint256& hashBlock, int conflicting_height, const uint256& hashTx);
- /* Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
+ /** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator>) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -686,6 +283,7 @@ private:
* Should be called with non-zero block_hash and posInBlock if this is for a transaction that is included in a block. */
void SyncTransaction(const CTransactionRef& tx, CWalletTx::Confirmation confirm, bool update_tx = true) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ /** WalletFlags set on this wallet. */
std::atomic<uint64_t> m_wallet_flags{0};
bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::string& strPurpose);
@@ -714,7 +312,7 @@ private:
*/
uint256 m_last_block_processed GUARDED_BY(cs_wallet);
- /* Height of last block processed is used by wallet to know depth of transactions
+ /** Height of last block processed is used by wallet to know depth of transactions
* without relying on Chain interface beyond asynchronous updates. For safety, we
* initialize it to -1. Height is a pointer on node's tip and doesn't imply
* that the wallet has scanned sequentially all blocks up to this one.
@@ -730,8 +328,15 @@ private:
bool CreateTransactionInternal(const std::vector<CRecipient>& vecSend, CTransactionRef& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, const CCoinControl& coin_control, FeeCalculation& fee_calc_out, bool sign);
+ /**
+ * Catch wallet up to current chain, scanning new blocks, updating the best
+ * block locator and m_last_block_processed, and registering for
+ * notifications about new blocks and transactions.
+ */
+ static bool AttachChain(const std::shared_ptr<CWallet>& wallet, interfaces::Chain& chain, bilingual_str& error, std::vector<bilingual_str>& warnings);
+
public:
- /*
+ /**
* Main wallet lock.
* This lock protects all the fields added by CWallet.
*/
@@ -745,11 +350,14 @@ public:
/**
* 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
+ * all coins from coin_control are selected; never select unconfirmed coins if they are not ours
+ * param@[out] setCoinsRet Populated with inputs including pre-selected inputs from
+ * coin_control and Coin Selection if successful.
+ * param@[out] nValueRet Total value of selected coins including pre-selected ones
+ * from coin_control and Coin Selection if successful.
*/
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 EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/** Get a name for this wallet for logging/debugging purposes.
*/
@@ -780,6 +388,8 @@ public:
/** Interface to assert chain access */
bool HaveChain() const { return m_chain ? true : false; }
+ /** Map from txid to CWalletTx for all transactions this wallet is
+ * interested in, including received and sent transactions. */
std::map<uint256, CWalletTx> mapWallet GUARDED_BY(cs_wallet);
typedef std::multimap<int64_t, CWalletTx*> TxItems;
@@ -791,6 +401,10 @@ public:
std::map<CTxDestination, CAddressBookData> m_address_book GUARDED_BY(cs_wallet);
const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ /** Set of Coins owned by this wallet that we won't try to spend from. A
+ * Coin may be locked if it has already been used to fund a transaction
+ * that hasn't confirmed yet. We wouldn't consider the Coin spent already,
+ * but also shouldn't try to use it again. */
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
/** Registered interfaces::Chain::Notifications handler. */
@@ -808,7 +422,7 @@ public:
/**
* populate vCoins with vector of available COutputs.
*/
- void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe = true, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ void AvailableCoins(std::vector<COutput>& vCoins, const CCoinControl* coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Return list of available coins and locked coins grouped by non-change output address.
@@ -825,9 +439,14 @@ public:
* small change; This method is stochastic for some inputs and upon
* completion the coin set and corresponding actual target value is
* assembled
+ * param@[in] coins Set of UTXOs to consider. These will be categorized into
+ * OutputGroups and filtered using eligibility_filter before
+ * selecting coins.
+ * param@[out] setCoinsRet Populated with the coins selected if successful.
+ * param@[out] nValueRet Used to return the total value of selected coins.
*/
bool SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibilityFilter& eligibility_filter, std::vector<COutput> coins,
- std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params, bool& bnb_used) const;
+ std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CoinSelectionParams& coin_selection_params) const;
bool IsSpent(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -835,7 +454,10 @@ public:
bool IsSpentKey(const uint256& hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void SetSpentKeyState(WalletBatch& batch, const uint256& hash, unsigned int n, bool used, std::set<CTxDestination>& tx_destinations) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, bool separate_coins, const CFeeRate& effective_feerate, const CFeeRate& long_term_feerate, const CoinEligibilityFilter& filter, bool positive_only) const;
+ std::vector<OutputGroup> GroupOutputs(const std::vector<COutput>& outputs, const CoinSelectionParams& coin_sel_params, const CoinEligibilityFilter& filter, bool positive_only) const;
+
+ /** Display address on an external signer. Returns false if external signer support is not compiled */
+ bool DisplayAddress(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -915,7 +537,7 @@ public:
//! Unset if no blocks were scanned due to read errors or the chain
//! being empty.
uint256 last_scanned_block;
- Optional<int> last_scanned_height;
+ std::optional<int> last_scanned_height;
//! Height of the most recent block that could not be scanned due to
//! read errors or pruning. Will be set if status is FAILURE, unset if
@@ -923,7 +545,7 @@ public:
//! USER_ABORT.
uint256 last_failed_block;
};
- ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
+ ScanResult ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate);
void transactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) override;
void ReacceptWalletTransactions() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
void ResendWalletTransactions();
@@ -938,16 +560,16 @@ public:
Balance GetBalance(int min_depth = 0, bool avoid_reuse = true) const;
CAmount GetAvailableBalance(const CCoinControl* coinControl = nullptr) const;
- OutputType TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
+ OutputType TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const;
/**
* Insert additional inputs into the transaction by
* calling CreateTransaction();
*/
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl);
- // Fetch the inputs and sign with SIGHASH_ALL.
+ /** Fetch the inputs and sign with SIGHASH_ALL. */
bool SignTransaction(CMutableTransaction& tx) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- // Sign the tx given the input coins and sighash.
+ /** Sign the tx given the input coins and sighash. */
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const;
@@ -1004,6 +626,8 @@ public:
CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE};
unsigned int m_confirm_target{DEFAULT_TX_CONFIRM_TARGET};
+ /** Allow Coin Selection to pick unconfirmed UTXOs that were sent from our own wallet if it
+ * cannot fund the transaction otherwise. */
bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE};
bool m_signal_rbf{DEFAULT_WALLET_RBF};
bool m_allow_fallback_fee{true}; //!< will be false if -fallbackfee=0
@@ -1014,7 +638,12 @@ public:
* Override with -fallbackfee
*/
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
+
+ /** If the cost to spend a change output at this feerate is greater than the value of the
+ * output itself, just drop it to fees. */
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
+
+ /** The maximum fee amount we're willing to pay to prioritize partial spend avoidance. */
CAmount m_max_aps_fee{DEFAULT_MAX_AVOIDPARTIALSPEND_FEE}; //!< note: this is absolute fee, not fee rate
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
/**
@@ -1023,7 +652,7 @@ public:
* (see -changetype option documentation and implementation in
* CWallet::TransactionChangeType for details).
*/
- Optional<OutputType> m_default_change_type{};
+ std::optional<OutputType> m_default_change_type{};
/** Absolute maximum transaction fee (in satoshis) used by default for the wallet */
CAmount m_default_max_tx_fee{DEFAULT_TRANSACTION_MAXFEE};
@@ -1069,7 +698,7 @@ public:
CAmount GetChange(const CTransaction& tx) const;
void chainStateFlushed(const CBlockLocator& loc) override;
- DBErrors LoadWallet(bool& fFirstRunRet);
+ DBErrors LoadWallet();
DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose);
@@ -1145,7 +774,7 @@ public:
bool MarkReplaced(const uint256& originalHash, const uint256& newHash);
/* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */
- static std::shared_ptr<CWallet> Create(interfaces::Chain& chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
+ static std::shared_ptr<CWallet> Create(interfaces::Chain* chain, const std::string& name, std::unique_ptr<WalletDatabase> database, uint64_t wallet_creation_flags, bilingual_str& error, std::vector<bilingual_str>& warnings);
/**
* Wallet post-init setup
@@ -1167,7 +796,7 @@ public:
* Obviously holding cs_main/cs_wallet when going into this call may cause
* deadlock
*/
- void BlockUntilSyncedToCurrentChain() const EXCLUSIVE_LOCKS_REQUIRED(!::cs_main, !cs_wallet);
+ void BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(::cs_main) EXCLUSIVE_LOCKS_REQUIRED(!cs_wallet);
/** set a single wallet flag */
void SetWalletFlag(uint64_t flags);
@@ -1322,12 +951,17 @@ public:
}
};
-// Calculate the size of the transaction assuming all signatures are max size
-// Use DummySignatureCreator, which inserts 71 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, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
+struct TxSize {
+ int64_t vsize{-1};
+ int64_t weight{-1};
+};
+
+/** Calculate the size of the transaction assuming all signatures are max size
+* Use DummySignatureCreator, which inserts 71 byte signatures everywhere.
+* NOTE: this requires that all inputs must be in mapWallet (eg the tx should
+* be IsAllFromMe). */
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, bool use_max_sig = false) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet);
+TxSize CalculateMaximumSignedTxSize(const CTransaction& tx, const CWallet* wallet, const std::vector<CTxOut>& txouts, bool use_max_sig = false);
//! Add wallet name to persistent configuration so it will be loaded on startup.
bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name);