Line data Source code
1 : // Copyright (c) 2014-2025 The Dash Core developers 2 : // Distributed under the MIT/X11 software license, see the accompanying 3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : 5 : #ifndef BITCOIN_COINJOIN_UTIL_H 6 : #define BITCOIN_COINJOIN_UTIL_H 7 : 8 : #include <wallet/coincontrol.h> 9 : #include <wallet/wallet.h> 10 : 11 : class CTransactionBuilder; 12 : struct bilingual_str; 13 : 14 : class CKeyHolder 15 : { 16 : private: 17 : wallet::ReserveDestination reserveDestination; 18 : CTxDestination dest; 19 : 20 : public: 21 : explicit CKeyHolder(wallet::CWallet* pwalletIn); 22 : CKeyHolder(CKeyHolder&&) = delete; 23 : CKeyHolder& operator=(CKeyHolder&&) = delete; 24 : void KeepKey(); 25 : void ReturnKey(); 26 : 27 : [[nodiscard]] CScript GetScriptForDestination() const; 28 : }; 29 : 30 : class CKeyHolderStorage 31 : { 32 : private: 33 : mutable Mutex cs_storage; 34 : std::vector<std::unique_ptr<CKeyHolder> > storage GUARDED_BY(cs_storage); 35 : 36 : public: 37 : CScript AddKey(wallet::CWallet* pwalletIn) EXCLUSIVE_LOCKS_REQUIRED(!cs_storage); 38 : void KeepAll() EXCLUSIVE_LOCKS_REQUIRED(!cs_storage); 39 : void ReturnAll() EXCLUSIVE_LOCKS_REQUIRED(!cs_storage); 40 : }; 41 : 42 : /** 43 : * @brief Used by CTransactionBuilder to represent its transaction outputs. 44 : * It creates a ReserveDestination for the given CWallet as destination. 45 : */ 46 : class CTransactionBuilderOutput 47 : { 48 : /// Used for amount updates 49 : CTransactionBuilder* pTxBuilder{nullptr}; 50 : /// Reserve key where the amount of this output will end up 51 : wallet::ReserveDestination dest; 52 : /// Amount this output will receive 53 : CAmount nAmount{0}; 54 : /// ScriptPubKey of this output 55 : CScript script; 56 : 57 : public: 58 : CTransactionBuilderOutput(CTransactionBuilder* pTxBuilderIn, wallet::CWallet& wallet, CAmount nAmountIn); 59 : CTransactionBuilderOutput(CTransactionBuilderOutput&&) = delete; 60 : CTransactionBuilderOutput& operator=(CTransactionBuilderOutput&&) = delete; 61 : /// Get the scriptPubKey of this output 62 203 : [[nodiscard]] CScript GetScript() const { return script; } 63 : /// Get the amount of this output 64 5363 : [[nodiscard]] CAmount GetAmount() const { return nAmount; } 65 : /// Try update the amount of this output. Returns true if it was successful and false if not (e.g. insufficient amount left). 66 : bool UpdateAmount(CAmount nAmount); 67 : /// Tell the wallet to remove the key used by this output from the keypool 68 101 : void KeepKey() { dest.KeepDestination(); } 69 : /// Tell the wallet to return the key used by this output to the keypool 70 0 : void ReturnKey() { dest.ReturnDestination(); } 71 : }; 72 : 73 : /** 74 : * @brief Enables simple transaction generation for a given CWallet object. The resulting 75 : * transaction's inputs are defined by the given CompactTallyItem. The outputs are 76 : * defined by CTransactionBuilderOutput. 77 : */ 78 : class CTransactionBuilder 79 : { 80 : /// Wallet the transaction will be build for 81 : wallet::CWallet& m_wallet; 82 : /// See CTransactionBuilder() for initialization 83 : wallet::CCoinControl coinControl; 84 : /// Dummy since we anyway use tallyItem's destination as change destination in coincontrol. 85 : /// Its a member just to make sure ReturnKey can be called in destructor just in case it gets generated/kept 86 : /// somewhere in CWallet code. 87 : wallet::ReserveDestination dummyReserveDestination; 88 : /// Contains all utxos available to generate this transactions. They are all from the same address. 89 : wallet::CompactTallyItem tallyItem; 90 : /// Contains the number of bytes required for a transaction with only the inputs of tallyItems, no outputs 91 : int nBytesBase{0}; 92 : /// Contains the number of bytes required to add one output 93 : int nBytesOutput{0}; 94 : /// Call KeepKey for all keys in destructor if fKeepKeys is true, call ReturnKey for all key if its false. 95 : bool fKeepKeys{false}; 96 : /// Protect vecOutputs 97 : mutable Mutex cs_outputs; 98 : /// Contains all outputs already added to the transaction 99 : std::vector<std::unique_ptr<CTransactionBuilderOutput>> vecOutputs GUARDED_BY(cs_outputs); 100 : /// Needed by CTransactionBuilderOutput::UpdateAmount to lock cs_outputs 101 : friend class CTransactionBuilderOutput; 102 : 103 : public: 104 : CTransactionBuilder(wallet::CWallet& wallet, const wallet::CompactTallyItem& tallyItemIn); 105 : ~CTransactionBuilder(); 106 : /// Check it would be possible to add a single output with the amount nAmount. Returns true if its possible and false if not. 107 : bool CouldAddOutput(CAmount nAmountOutput) const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 108 : /// Check if its possible to add multiple outputs as vector of amounts. Returns true if its possible to add all of them and false if not. 109 : bool CouldAddOutputs(const std::vector<CAmount>& vecOutputAmounts) const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 110 : /// Add an output with the amount nAmount. Returns a pointer to the output if it could be added and nullptr if not due to insufficient amount left. 111 : CTransactionBuilderOutput* AddOutput(CAmount nAmountOutput = 0) EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 112 : /// Get amount we had available when we started 113 119 : CAmount GetAmountInitial() const { return tallyItem.nAmount; } 114 : /// Get the amount currently left to add more outputs. Does respect fees. 115 11 : CAmount GetAmountLeft() const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs) 116 11 : { return GetAmountInitial() - GetAmountUsed() - GetFee(GetBytesTotal()); } 117 : /// Check if an amounts should be considered as dust 118 : bool IsDust(CAmount nAmount) const; 119 : /// Get the total number of added outputs 120 5 : int CountOutputs() const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs) 121 : { 122 5 : LOCK(cs_outputs); 123 5 : return vecOutputs.size(); 124 5 : } 125 : /// Create and Commit the transaction to the wallet 126 : bool Commit(bilingual_str& strResult) EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 127 : /// Convert to a string 128 : std::string ToString() const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 129 : 130 : private: 131 : /// Clear the output vector and keep/return the included keys depending on the value of fKeepKeys 132 : void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 133 : /// Get the total number of bytes used already by this transaction 134 : unsigned int GetBytesTotal() const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 135 : /// Helper to calculate static amount left by simply subtracting an used amount and a fee from a provided initial amount. 136 : static CAmount GetAmountLeft(CAmount nAmountInitial, CAmount nAmountUsed, CAmount nFee); 137 : /// Get the amount currently used by added outputs. Does not include fees. 138 : CAmount GetAmountUsed() const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 139 : /// Get fees based on the number of bytes and the feerate set in CoinControl. 140 : /// NOTE: To get the total transaction fee this should only be called once with the total number of bytes for the transaction to avoid 141 : /// calling CFeeRate::GetFee multiple times with subtotals as this may add rounding errors with each further call. 142 : CAmount GetFee(unsigned int nBytes) const; 143 : /// Helper to get GetSizeOfCompactSizeDiff(vecOutputs.size(), vecOutputs.size() + nAdd) 144 : int GetSizeOfCompactSizeDiff(size_t nAdd) const EXCLUSIVE_LOCKS_REQUIRED(!cs_outputs); 145 : }; 146 : 147 : #endif // BITCOIN_COINJOIN_UTIL_H