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.h125
1 files changed, 93 insertions, 32 deletions
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index e8c18dbb67..cbd5008366 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -7,14 +7,15 @@
#define BITCOIN_WALLET_WALLET_H
#include <consensus/amount.h>
-#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/handler.h>
+#include <interfaces/wallet.h>
#include <logging.h>
#include <outputtype.h>
#include <policy/feerate.h>
#include <psbt.h>
#include <tinyformat.h>
+#include <util/fs.h>
#include <util/hasher.h>
#include <util/message.h>
#include <util/result.h>
@@ -145,8 +146,6 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
{"external_signer", WALLET_FLAG_EXTERNAL_SIGNER}
};
-extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
-
/** A wrapper to reserve an address from a wallet
*
* ReserveDestination is used to reserve an address.
@@ -200,28 +199,69 @@ public:
void KeepDestination();
};
-/** Address book data */
-class CAddressBookData
+/**
+ * Address book data.
+ */
+struct CAddressBookData
{
-private:
- bool m_change{true};
- std::string m_label;
-public:
- std::string purpose;
+ /**
+ * Address label which is always nullopt for change addresses. For sending
+ * and receiving addresses, it will be set to an arbitrary label string
+ * provided by the user, or to "", which is the default label. The presence
+ * or absence of a label is used to distinguish change addresses from
+ * non-change addresses by wallet transaction listing and fee bumping code.
+ */
+ std::optional<std::string> label;
- CAddressBookData() : purpose("unknown") {}
+ /**
+ * Address purpose which was originally recorded for payment protocol
+ * support but now serves as a cached IsMine value. Wallet code should
+ * not rely on this field being set.
+ */
+ std::optional<AddressPurpose> purpose;
- typedef std::map<std::string, std::string> StringMap;
- StringMap destdata;
+ /**
+ * Whether coins with this address have previously been spent. Set when the
+ * the wallet avoid_reuse option is enabled and this is an IsMine address
+ * that has already received funds and spent them. This is used during coin
+ * selection to increase privacy by not creating different transactions
+ * that spend from the same addresses.
+ */
+ bool previously_spent{false};
- bool IsChange() const { return m_change; }
- const std::string& GetLabel() const { return m_label; }
- void SetLabel(const std::string& label) {
- m_change = false;
- m_label = label;
- }
+ /**
+ * Map containing data about previously generated receive requests
+ * requesting funds to be sent to this address. Only present for IsMine
+ * addresses. Map keys are decimal numbers uniquely identifying each
+ * request, and map values are serialized RecentRequestEntry objects
+ * containing BIP21 URI information including message and amount.
+ */
+ std::map<std::string, std::string> receive_requests{};
+
+ /** Accessor methods. */
+ bool IsChange() const { return !label.has_value(); }
+ std::string GetLabel() const { return label ? *label : std::string{}; }
+ void SetLabel(std::string name) { label = std::move(name); }
};
+inline std::string PurposeToString(AddressPurpose p)
+{
+ switch(p) {
+ case AddressPurpose::RECEIVE: return "receive";
+ case AddressPurpose::SEND: return "send";
+ case AddressPurpose::REFUND: return "refund";
+ } // no default case so the compiler will warn when a new enum as added
+ assert(false);
+}
+
+inline std::optional<AddressPurpose> PurposeFromString(std::string_view s)
+{
+ if (s == "receive") return AddressPurpose::RECEIVE;
+ else if (s == "send") return AddressPurpose::SEND;
+ else if (s == "refund") return AddressPurpose::REFUND;
+ return {};
+}
+
struct CRecipient
{
CScript scriptPubKey;
@@ -244,7 +284,7 @@ private:
std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver
std::atomic<bool> m_attaching_chain{false};
std::atomic<bool> m_scanning_with_passphrase{false};
- std::atomic<int64_t> m_scanning_start{0};
+ std::atomic<SteadyClock::time_point> m_scanning_start{SteadyClock::time_point{}};
std::atomic<double> m_scanning_progress{0};
friend class WalletRescanReserver;
@@ -259,6 +299,10 @@ private:
// Local time that the tip block was received. Used to schedule wallet rebroadcasts.
std::atomic<int64_t> m_best_block_time {0};
+ // First created key time. Used to skip blocks prior to this time.
+ // 'std::numeric_limits<int64_t>::max()' if wallet is blank.
+ std::atomic<int64_t> m_birth_time{std::numeric_limits<int64_t>::max()};
+
/**
* Used to keep track of spent outpoints, and
* detect and report conflicts (double-spends or
@@ -290,6 +334,13 @@ private:
/** 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);
+ enum class TxUpdate { UNCHANGED, CHANGED, NOTIFY_CHANGED };
+
+ using TryUpdatingStateFn = std::function<TxUpdate(CWalletTx& wtx)>;
+
+ /** Mark a transaction (and its in-wallet descendants) as a particular tx state. */
+ void RecursiveUpdateTxState(const uint256& tx_hash, const TryUpdatingStateFn& try_updating_state) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+
/** Mark a transaction's inputs dirty, thus forcing the outputs to be recomputed */
void MarkInputsDirty(const CTransactionRef& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -300,7 +351,7 @@ private:
/** 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);
+ bool SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& address, const std::string& strName, const std::optional<AddressPurpose>& strPurpose);
//! Unsets a wallet flag and saves it to disk
void UnsetWalletFlagWithDB(WalletBatch& batch, uint64_t flag);
@@ -340,6 +391,10 @@ private:
// ScriptPubKeyMan::GetID. In many cases it will be the hash of an internal structure
std::map<uint256, std::unique_ptr<ScriptPubKeyMan>> m_spk_managers;
+ // Appends spk managers into the main 'm_spk_managers'.
+ // Must be the only method adding data to it.
+ void AddScriptPubKeyMan(const uint256& id, std::unique_ptr<ScriptPubKeyMan> spkm_man);
+
/**
* Catch wallet up to current chain, scanning new blocks, updating the best
* block locator and m_last_block_processed, and registering for
@@ -465,7 +520,7 @@ public:
bool IsAbortingRescan() const { return fAbortRescan; }
bool IsScanning() const { return fScanningWallet; }
bool IsScanningWithPassphrase() const { return m_scanning_with_passphrase; }
- int64_t ScanningDuration() const { return fScanningWallet ? GetTimeMillis() - m_scanning_start : 0; }
+ SteadyClock::duration ScanningDuration() const { return fScanningWallet ? SteadyClock::now() - m_scanning_start.load() : SteadyClock::duration{}; }
double ScanningProgress() const { return fScanningWallet ? (double) m_scanning_progress : 0; }
//! Upgrade stored CKeyMetadata objects to store key origin info as KeyOriginInfo
@@ -476,8 +531,10 @@ public:
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; return true; }
- //! Adds a destination data tuple to the store, without saving it to disk
- void LoadDestData(const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ //! Marks destination as previously spent.
+ void LoadAddressPreviouslySpent(const CTxDestination& dest) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ //! Appends payment request to destination.
+ void LoadAddressReceiveRequest(const CTxDestination& dest, const std::string& id, const std::string& request) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! 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 GUARDED_BY(cs_wallet){0};
@@ -599,6 +656,9 @@ public:
bool ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool ImportScriptPubKeys(const std::string& label, const std::set<CScript>& script_pub_keys, const bool have_solving_data, const bool apply_label, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ /** Updates wallet birth time if 'new_birth_time' is below it */
+ void FirstKeyTimeChanged(const ScriptPubKeyMan* spkm, int64_t new_birth_time);
+
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
@@ -664,13 +724,13 @@ public:
/**
* Retrieve all the known labels in the address book
*/
- std::set<std::string> ListAddrBookLabels(const std::string& purpose) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ std::set<std::string> ListAddrBookLabels(const std::optional<AddressPurpose> purpose) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
* Walk-through the address book entries.
* Stops when the provided 'ListAddrBookFunc' returns false.
*/
- using ListAddrBookFunc = std::function<void(const CTxDestination& dest, const std::string& label, const std::string& purpose, bool is_change)>;
+ using ListAddrBookFunc = std::function<void(const CTxDestination& dest, const std::string& label, bool is_change, const std::optional<AddressPurpose> purpose)>;
void ForEachAddrBookEntry(const ListAddrBookFunc& func) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
/**
@@ -700,15 +760,16 @@ public:
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);
+ bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::optional<AddressPurpose>& purpose);
bool DelAddressBook(const CTxDestination& address);
- bool IsAddressUsed(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
- bool SetAddressUsed(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool IsAddressPreviouslySpent(const CTxDestination& dest) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool SetAddressPreviouslySpent(WalletBatch& batch, const CTxDestination& dest, bool used) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::vector<std::string> GetAddressReceiveRequests() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
bool SetAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
+ bool EraseAddressReceiveRequest(WalletBatch& batch, const CTxDestination& dest, const std::string& id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
unsigned int GetKeyPoolSize() const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
@@ -739,7 +800,7 @@ public:
*/
boost::signals2::signal<void(const CTxDestination& address,
const std::string& label, bool isMine,
- const std::string& purpose, ChangeType status)>
+ AddressPurpose purpose, ChangeType status)>
NotifyAddressBookChanged;
/**
@@ -971,7 +1032,7 @@ public:
return false;
}
m_wallet.m_scanning_with_passphrase.exchange(with_passphrase);
- m_wallet.m_scanning_start = GetTimeMillis();
+ m_wallet.m_scanning_start = SteadyClock::now();
m_wallet.m_scanning_progress = 0;
m_could_reserve = true;
return true;
@@ -1013,7 +1074,7 @@ struct MigrationResult {
};
//! Do all steps to migrate a legacy wallet to a descriptor wallet
-util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context);
+[[nodiscard]] util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& wallet_name, const SecureString& passphrase, WalletContext& context);
} // namespace wallet
#endif // BITCOIN_WALLET_WALLET_H