diff options
Diffstat (limited to 'src/wallet/wallet.h')
-rw-r--r-- | src/wallet/wallet.h | 125 |
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 |