diff options
Diffstat (limited to 'src/wallet')
74 files changed, 507 insertions, 130 deletions
diff --git a/src/wallet/bdb.cpp b/src/wallet/bdb.cpp index 5b264ac4ad..49f0abf9e7 100644 --- a/src/wallet/bdb.cpp +++ b/src/wallet/bdb.cpp @@ -3,6 +3,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <fs.h> #include <wallet/bdb.h> #include <wallet/db.h> @@ -15,6 +16,7 @@ #include <sys/stat.h> #endif +namespace wallet { namespace { //! Make sure database has a unique fileid within the environment. If it @@ -619,12 +621,12 @@ bool BerkeleyDatabase::Backup(const std::string& strDest) const pathDest /= fs::PathFromString(strFile); try { - if (fs::equivalent(pathSrc, pathDest)) { + if (fs::exists(pathDest) && fs::equivalent(pathSrc, pathDest)) { LogPrintf("cannot backup to wallet source file %s\n", fs::PathToString(pathDest)); return false; } - fs::copy_file(pathSrc, pathDest, fs::copy_option::overwrite_if_exists); + fs::copy_file(pathSrc, pathDest, fs::copy_options::overwrite_existing); LogPrintf("copied %s to %s\n", strFile, fs::PathToString(pathDest)); return true; } catch (const fs::filesystem_error& e) { @@ -680,10 +682,10 @@ bool BerkeleyBatch::ReadAtCursor(CDataStream& ssKey, CDataStream& ssValue, bool& // Convert to streams ssKey.SetType(SER_DISK); ssKey.clear(); - ssKey.write((char*)datKey.get_data(), datKey.get_size()); + ssKey.write({BytePtr(datKey.get_data()), datKey.get_size()}); ssValue.SetType(SER_DISK); ssValue.clear(); - ssValue.write((char*)datValue.get_data(), datValue.get_size()); + ssValue.write({BytePtr(datValue.get_data()), datValue.get_size()}); return true; } @@ -755,7 +757,7 @@ bool BerkeleyBatch::ReadKey(CDataStream&& key, CDataStream& value) SafeDbt datValue; int ret = pdb->get(activeTxn, datKey, datValue, 0); if (ret == 0 && datValue.get_data() != nullptr) { - value.write((char*)datValue.get_data(), datValue.get_size()); + value.write({BytePtr(datValue.get_data()), datValue.get_size()}); return true; } return false; @@ -846,3 +848,4 @@ std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, con status = DatabaseStatus::SUCCESS; return db; } +} // namespace wallet diff --git a/src/wallet/bdb.h b/src/wallet/bdb.h index 0cf9843cc8..b924890d81 100644 --- a/src/wallet/bdb.h +++ b/src/wallet/bdb.h @@ -31,6 +31,7 @@ struct bilingual_str; +namespace wallet { static const unsigned int DEFAULT_WALLET_DBLOGSIZE = 100; static const bool DEFAULT_WALLET_PRIVDB = true; @@ -229,5 +230,6 @@ bool BerkeleyDatabaseSanityCheck(); //! Return object giving access to Berkeley database at specified path. std::unique_ptr<BerkeleyDatabase> MakeBerkeleyDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); +} // namespace wallet #endif // BITCOIN_WALLET_BDB_H diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp index bf582e1cc1..3b3c1f8da4 100644 --- a/src/wallet/coincontrol.cpp +++ b/src/wallet/coincontrol.cpp @@ -6,7 +6,9 @@ #include <util/system.h> +namespace wallet { CCoinControl::CCoinControl() { m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS); } +} // namespace wallet diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index cb30b09b87..65a5c83366 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -18,6 +18,7 @@ #include <map> #include <set> +namespace wallet { const int DEFAULT_MIN_DEPTH = 0; const int DEFAULT_MAX_DEPTH = 9999999; @@ -114,9 +115,29 @@ public: vOutpoints.assign(setSelected.begin(), setSelected.end()); } + void SetInputWeight(const COutPoint& outpoint, int64_t weight) + { + m_input_weights[outpoint] = weight; + } + + bool HasInputWeight(const COutPoint& outpoint) const + { + return m_input_weights.count(outpoint) > 0; + } + + int64_t GetInputWeight(const COutPoint& outpoint) const + { + auto it = m_input_weights.find(outpoint); + assert(it != m_input_weights.end()); + return it->second; + } + private: std::set<COutPoint> setSelected; std::map<COutPoint, CTxOut> m_external_txouts; + //! Map of COutPoints to the maximum weight for that input + std::map<COutPoint, int64_t> m_input_weights; }; +} // namespace wallet #endif // BITCOIN_WALLET_COINCONTROL_H diff --git a/src/wallet/coinselection.cpp b/src/wallet/coinselection.cpp index 3b48a8957e..23faad027f 100644 --- a/src/wallet/coinselection.cpp +++ b/src/wallet/coinselection.cpp @@ -13,6 +13,7 @@ #include <numeric> #include <optional> +namespace wallet { // Descending order comparator struct { bool operator()(const OutputGroup& a, const OutputGroup& b) const @@ -429,3 +430,4 @@ bool SelectionResult::operator<(SelectionResult other) const // As this operator is only used in std::min_element, we want the result that has more inputs when waste are equal. return *m_waste < *other.m_waste || (*m_waste == *other.m_waste && m_selected_inputs.size() > other.m_selected_inputs.size()); } +} // namespace wallet diff --git a/src/wallet/coinselection.h b/src/wallet/coinselection.h index a0001351f7..496a026999 100644 --- a/src/wallet/coinselection.h +++ b/src/wallet/coinselection.h @@ -12,6 +12,7 @@ #include <optional> +namespace wallet { //! target minimum change amount static constexpr CAmount MIN_CHANGE{COIN / 100}; //! final minimum change amount after paying for fees @@ -249,5 +250,6 @@ std::optional<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& ut // Original coin selection algorithm as a fallback std::optional<SelectionResult> KnapsackSolver(std::vector<OutputGroup>& groups, const CAmount& nTargetValue); +} // namespace wallet #endif // BITCOIN_WALLET_COINSELECTION_H diff --git a/src/wallet/context.cpp b/src/wallet/context.cpp index 09b2f30467..800aa5bf9c 100644 --- a/src/wallet/context.cpp +++ b/src/wallet/context.cpp @@ -4,5 +4,7 @@ #include <wallet/context.h> +namespace wallet { WalletContext::WalletContext() {} WalletContext::~WalletContext() {} +} // namespace wallet diff --git a/src/wallet/context.h b/src/wallet/context.h index 0c11182ee6..57a6ed77f7 100644 --- a/src/wallet/context.h +++ b/src/wallet/context.h @@ -13,12 +13,13 @@ #include <vector> class ArgsManager; -class CWallet; namespace interfaces { class Chain; class Wallet; } // namespace interfaces +namespace wallet { +class CWallet; using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>; //! WalletContext struct containing references to state shared between CWallet @@ -46,5 +47,6 @@ struct WalletContext { WalletContext(); ~WalletContext(); }; +} // namespace wallet #endif // BITCOIN_WALLET_CONTEXT_H diff --git a/src/wallet/crypter.cpp b/src/wallet/crypter.cpp index 683b55a408..cd414b3d44 100644 --- a/src/wallet/crypter.cpp +++ b/src/wallet/crypter.cpp @@ -10,6 +10,7 @@ #include <vector> +namespace wallet { int CCrypter::BytesToKeySHA512AES(const std::vector<unsigned char>& chSalt, const SecureString& strKeyData, int count, unsigned char *key,unsigned char *iv) const { // This mimics the behavior of openssl's EVP_BytesToKey with an aes256cbc @@ -136,3 +137,4 @@ bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned ch key.Set(vchSecret.begin(), vchSecret.end(), vchPubKey.IsCompressed()); return key.VerifyPubKey(vchPubKey); } +} // namespace wallet diff --git a/src/wallet/crypter.h b/src/wallet/crypter.h index f7325541a9..4d325c7557 100644 --- a/src/wallet/crypter.h +++ b/src/wallet/crypter.h @@ -10,6 +10,7 @@ #include <script/signingprovider.h> +namespace wallet { const unsigned int WALLET_CRYPTO_KEY_SIZE = 32; const unsigned int WALLET_CRYPTO_SALT_SIZE = 8; const unsigned int WALLET_CRYPTO_IV_SIZE = 16; @@ -105,5 +106,6 @@ public: bool EncryptSecret(const CKeyingMaterial& vMasterKey, const CKeyingMaterial &vchPlaintext, const uint256& nIV, std::vector<unsigned char> &vchCiphertext); bool DecryptSecret(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCiphertext, const uint256& nIV, CKeyingMaterial& vchPlaintext); bool DecryptKey(const CKeyingMaterial& vMasterKey, const std::vector<unsigned char>& vchCryptedSecret, const CPubKey& vchPubKey, CKey& key); +} // namespace wallet #endif // BITCOIN_WALLET_CRYPTER_H diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 6437e1d7f0..0ed2658129 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -8,17 +8,22 @@ #include <logging.h> #include <wallet/db.h> +#include <exception> +#include <fstream> #include <string> +#include <system_error> +#include <vector> +namespace wallet { std::vector<fs::path> ListDatabases(const fs::path& wallet_dir) { std::vector<fs::path> paths; - boost::system::error_code ec; + std::error_code ec; for (auto it = fs::recursive_directory_iterator(wallet_dir, ec); it != fs::recursive_directory_iterator(); it.increment(ec)) { if (ec) { if (fs::is_directory(*it)) { - it.no_push(); + it.disable_recursion_pending(); LogPrintf("%s: %s %s -- skipping.\n", __func__, ec.message(), fs::PathToString(it->path())); } else { LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(it->path())); @@ -29,11 +34,11 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir) try { const fs::path path{it->path().lexically_relative(wallet_dir)}; - if (it->status().type() == fs::directory_file && + if (it->status().type() == fs::file_type::directory && (IsBDBFile(BDBDataFile(it->path())) || IsSQLiteFile(SQLiteDataFile(it->path())))) { // Found a directory which contains wallet.dat btree file, add it as a wallet. paths.emplace_back(path); - } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBDBFile(it->path())) { + } else if (it.depth() == 0 && it->symlink_status().type() == fs::file_type::regular && IsBDBFile(it->path())) { if (it->path().filename() == "wallet.dat") { // Found top-level wallet.dat btree file, add top level directory "" // as a wallet. @@ -48,7 +53,7 @@ std::vector<fs::path> ListDatabases(const fs::path& wallet_dir) } } catch (const std::exception& e) { LogPrintf("%s: Error scanning %s: %s\n", __func__, fs::PathToString(it->path()), e.what()); - it.no_push(); + it.disable_recursion_pending(); } } @@ -80,12 +85,12 @@ bool IsBDBFile(const fs::path& path) // A Berkeley DB Btree file has at least 4K. // This check also prevents opening lock files. - boost::system::error_code ec; + std::error_code ec; auto size = fs::file_size(path, ec); if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path)); if (size < 4096) return false; - fsbridge::ifstream file(path, std::ios::binary); + std::ifstream file{path, std::ios::binary}; if (!file.is_open()) return false; file.seekg(12, std::ios::beg); // Magic bytes start at offset 12 @@ -104,12 +109,12 @@ bool IsSQLiteFile(const fs::path& path) if (!fs::exists(path)) return false; // A SQLite Database file is at least 512 bytes. - boost::system::error_code ec; + std::error_code ec; auto size = fs::file_size(path, ec); if (ec) LogPrintf("%s: %s %s\n", __func__, ec.message(), fs::PathToString(path)); if (size < 512) return false; - fsbridge::ifstream file(path, std::ios::binary); + std::ifstream file{path, std::ios::binary}; if (!file.is_open()) return false; // Magic is at beginning and is 16 bytes long @@ -132,3 +137,4 @@ bool IsSQLiteFile(const fs::path& path) // Check the application id matches our network magic return memcmp(Params().MessageStart(), app_id, 4) == 0; } +} // namespace wallet diff --git a/src/wallet/db.h b/src/wallet/db.h index 7acc27e2b4..5825b00e3a 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -18,6 +18,7 @@ struct bilingual_str; +namespace wallet { void SplitWalletPath(const fs::path& wallet_path, fs::path& env_directory, std::string& database_filename); /** RAII class that provides access to a WalletDatabase */ @@ -232,5 +233,6 @@ fs::path BDBDataFile(const fs::path& path); fs::path SQLiteDataFile(const fs::path& path); bool IsBDBFile(const fs::path& path); bool IsSQLiteFile(const fs::path& path); +} // namespace wallet #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/dump.cpp b/src/wallet/dump.cpp index 9a4c201c4a..6d8508fc72 100644 --- a/src/wallet/dump.cpp +++ b/src/wallet/dump.cpp @@ -4,9 +4,18 @@ #include <wallet/dump.h> +#include <fs.h> #include <util/translation.h> #include <wallet/wallet.h> +#include <algorithm> +#include <fstream> +#include <memory> +#include <string> +#include <utility> +#include <vector> + +namespace wallet { static const std::string DUMP_MAGIC = "BITCOIN_CORE_WALLET_DUMP"; uint32_t DUMP_VERSION = 1; @@ -25,7 +34,7 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error) error = strprintf(_("File %s already exists. If you are sure this is what you want, move it out of the way first."), fs::PathToString(path)); return false; } - fsbridge::ofstream dump_file; + std::ofstream dump_file; dump_file.open(path); if (dump_file.fail()) { error = strprintf(_("Unable to open %s for writing"), fs::PathToString(path)); @@ -46,12 +55,12 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error) // Write out a magic string with version std::string line = strprintf("%s,%u\n", DUMP_MAGIC, DUMP_VERSION); dump_file.write(line.data(), line.size()); - hasher.write(line.data(), line.size()); + hasher.write(MakeByteSpan(line)); // Write out the file format line = strprintf("%s,%s\n", "format", db.Format()); dump_file.write(line.data(), line.size()); - hasher.write(line.data(), line.size()); + hasher.write(MakeByteSpan(line)); if (ret) { @@ -72,7 +81,7 @@ bool DumpWallet(CWallet& wallet, bilingual_str& error) std::string value_str = HexStr(ss_value); line = strprintf("%s,%s\n", key_str, value_str); dump_file.write(line.data(), line.size()); - hasher.write(line.data(), line.size()); + hasher.write(MakeByteSpan(line)); } } @@ -120,7 +129,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling error = strprintf(_("Dump file %s does not exist."), fs::PathToString(dump_path)); return false; } - fsbridge::ifstream dump_file(dump_path); + std::ifstream dump_file{dump_path}; // Compute the checksum CHashWriter hasher(0, 0); @@ -149,7 +158,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling return false; } std::string magic_hasher_line = strprintf("%s,%s\n", magic_key, version_value); - hasher.write(magic_hasher_line.data(), magic_hasher_line.size()); + hasher.write(MakeByteSpan(magic_hasher_line)); // Get the stored file format std::string format_key; @@ -180,7 +189,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling warnings.push_back(strprintf(_("Warning: Dumpfile wallet format \"%s\" does not match command line specified format \"%s\"."), format_value, file_format)); } std::string format_hasher_line = strprintf("%s,%s\n", format_key, format_value); - hasher.write(format_hasher_line.data(), format_hasher_line.size()); + hasher.write(MakeByteSpan(format_hasher_line)); DatabaseOptions options; DatabaseStatus status; @@ -224,7 +233,7 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling } std::string line = strprintf("%s,%s\n", key, value); - hasher.write(line.data(), line.size()); + hasher.write(MakeByteSpan(line)); if (key.empty() || value.empty()) { continue; @@ -284,3 +293,4 @@ bool CreateFromDump(const std::string& name, const fs::path& wallet_path, biling return ret; } +} // namespace wallet diff --git a/src/wallet/dump.h b/src/wallet/dump.h index d0a4f5ef1d..a879c4db35 100644 --- a/src/wallet/dump.h +++ b/src/wallet/dump.h @@ -7,11 +7,15 @@ #include <fs.h> -class CWallet; +#include <string> +#include <vector> struct bilingual_str; +namespace wallet { +class CWallet; bool DumpWallet(CWallet& wallet, bilingual_str& error); bool CreateFromDump(const std::string& name, const fs::path& wallet_path, bilingual_str& error, std::vector<bilingual_str>& warnings); +} // namespace wallet #endif // BITCOIN_WALLET_DUMP_H diff --git a/src/wallet/external_signer_scriptpubkeyman.cpp b/src/wallet/external_signer_scriptpubkeyman.cpp index ad89478874..9d5f58b784 100644 --- a/src/wallet/external_signer_scriptpubkeyman.cpp +++ b/src/wallet/external_signer_scriptpubkeyman.cpp @@ -13,6 +13,7 @@ #include <utility> #include <vector> +namespace wallet { bool ExternalSignerScriptPubKeyMan::SetupDescriptor(std::unique_ptr<Descriptor> desc) { LOCK(cs_desc_man); @@ -82,3 +83,4 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup return TransactionError::OK; } +} // namespace wallet diff --git a/src/wallet/external_signer_scriptpubkeyman.h b/src/wallet/external_signer_scriptpubkeyman.h index 92c1ccb401..9918979a81 100644 --- a/src/wallet/external_signer_scriptpubkeyman.h +++ b/src/wallet/external_signer_scriptpubkeyman.h @@ -9,6 +9,7 @@ #include <memory> +namespace wallet { class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan { public: @@ -30,4 +31,5 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override; }; +} // namespace wallet #endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 242a3c2847..3552c14160 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -16,6 +16,7 @@ #include <wallet/spend.h> #include <wallet/wallet.h> +namespace wallet { //! Check whether transaction has descendant in wallet or mempool, or has been //! mined, or conflicts with a mined transaction. Return a feebumper::Result. static feebumper::Result PreconditionChecks(const CWallet& wallet, const CWalletTx& wtx, std::vector<bilingual_str>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) @@ -282,3 +283,4 @@ Result CommitTransaction(CWallet& wallet, const uint256& txid, CMutableTransacti } } // namespace feebumper +} // namespace wallet diff --git a/src/wallet/feebumper.h b/src/wallet/feebumper.h index 50577c9d3e..191878a137 100644 --- a/src/wallet/feebumper.h +++ b/src/wallet/feebumper.h @@ -7,13 +7,15 @@ #include <primitives/transaction.h> -class CWallet; -class CWalletTx; class uint256; -class CCoinControl; enum class FeeEstimateMode; struct bilingual_str; +namespace wallet { +class CCoinControl; +class CWallet; +class CWalletTx; + namespace feebumper { enum class Result @@ -54,5 +56,6 @@ Result CommitTransaction(CWallet& wallet, uint256& bumped_txid); } // namespace feebumper +} // namespace wallet #endif // BITCOIN_WALLET_FEEBUMPER_H diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp index 429101e774..6f81fa30a1 100644 --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -9,6 +9,7 @@ #include <wallet/wallet.h> +namespace wallet { CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes) { return GetRequiredFeeRate(wallet).GetFee(nTxBytes); @@ -90,3 +91,4 @@ CFeeRate GetDiscardRate(const CWallet& wallet) discard_rate = std::max(discard_rate, wallet.chain().relayDustFee()); return discard_rate; } +} // namespace wallet diff --git a/src/wallet/fees.h b/src/wallet/fees.h index ad35670d4e..af7f759553 100644 --- a/src/wallet/fees.h +++ b/src/wallet/fees.h @@ -8,11 +8,13 @@ #include <consensus/amount.h> -class CCoinControl; class CFeeRate; -class CWallet; struct FeeCalculation; +namespace wallet { +class CCoinControl; +class CWallet; + /** * Return the minimum required absolute fee for this size * based on the required fee rate @@ -41,5 +43,6 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr * Return the maximum feerate for discarding change. */ CFeeRate GetDiscardRate(const CWallet& wallet); +} // namespace wallet #endif // BITCOIN_WALLET_FEES_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 2f73ec3d4c..7a83dbc35d 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -23,6 +23,9 @@ #include <wallet/wallet.h> #include <walletinitinterface.h> +using node::NodeContext; + +namespace wallet { class WalletInit : public WalletInitInterface { public: @@ -39,8 +42,6 @@ public: void Construct(NodeContext& node) const override; }; -const WalletInitInterface& g_wallet_init_interface = WalletInit(); - void WalletInit::AddWalletOptions(ArgsManager& argsman) const { argsman.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", \"bech32\", or \"bech32m\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET); @@ -137,3 +138,6 @@ void WalletInit::Construct(NodeContext& node) const node.wallet_loader = wallet_loader.get(); node.chain_clients.emplace_back(std::move(wallet_loader)); } +} // namespace wallet + +const WalletInitInterface& g_wallet_init_interface = wallet::WalletInit(); diff --git a/src/wallet/interfaces.cpp b/src/wallet/interfaces.cpp index 64775a5ddb..9083c304b2 100644 --- a/src/wallet/interfaces.cpp +++ b/src/wallet/interfaces.cpp @@ -90,7 +90,6 @@ WalletTxStatus MakeWalletTxStatus(const CWallet& wallet, const CWalletTx& wtx) result.depth_in_main_chain = wallet.GetTxDepthInMainChain(wtx); result.time_received = wtx.nTimeReceived; result.lock_time = wtx.tx->nLockTime; - result.is_final = wallet.chain().checkFinalTx(*wtx.tx); result.is_trusted = CachedTxIsTrusted(wallet, wtx); result.is_abandoned = wtx.isAbandoned(); result.is_coinbase = wtx.IsCoinBase(); @@ -557,7 +556,7 @@ public: options.require_existing = true; return MakeWallet(m_context, LoadWallet(m_context, name, true /* load_on_start */, options, status, error, warnings)); } - std::unique_ptr<Wallet> restoreWallet(const std::string& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) override + std::unique_ptr<Wallet> restoreWallet(const fs::path& backup_file, const std::string& wallet_name, bilingual_str& error, std::vector<bilingual_str>& warnings) override { DatabaseStatus status; @@ -598,7 +597,7 @@ public: } // namespace wallet namespace interfaces { -std::unique_ptr<Wallet> MakeWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(context, wallet) : nullptr; } +std::unique_ptr<Wallet> MakeWallet(wallet::WalletContext& context, const std::shared_ptr<wallet::CWallet>& wallet) { return wallet ? std::make_unique<wallet::WalletImpl>(context, wallet) : nullptr; } std::unique_ptr<WalletLoader> MakeWalletLoader(Chain& chain, ArgsManager& args) { diff --git a/src/wallet/ismine.h b/src/wallet/ismine.h index 8527b460df..4ef4ef98ac 100644 --- a/src/wallet/ismine.h +++ b/src/wallet/ismine.h @@ -12,9 +12,11 @@ #include <cstdint> #include <type_traits> -class CWallet; class CScript; +namespace wallet { +class CWallet; + /** * IsMine() return codes, which depend on ScriptPubKeyMan implementation. * Not every ScriptPubKeyMan covers all types, please refer to @@ -66,5 +68,6 @@ struct CachableAmount m_value[filter] = value; } }; +} // namespace wallet #endif // BITCOIN_WALLET_ISMINE_H diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 4d59e17709..4949ed7dc9 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -19,6 +19,9 @@ #include <univalue.h> +#include <system_error> + +namespace wallet { bool VerifyWallets(WalletContext& context) { interfaces::Chain& chain = *context.chain; @@ -26,7 +29,7 @@ bool VerifyWallets(WalletContext& context) if (args.IsArgSet("-walletdir")) { fs::path wallet_dir = fs::PathFromString(args.GetArg("-walletdir", "")); - boost::system::error_code error; + std::error_code error; // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); if (error || !fs::exists(wallet_dir)) { @@ -169,3 +172,4 @@ void UnloadWallets(WalletContext& context) UnloadWallet(std::move(wallet)); } } +} // namespace wallet diff --git a/src/wallet/load.h b/src/wallet/load.h index e207bc2e09..5c2bbdabe4 100644 --- a/src/wallet/load.h +++ b/src/wallet/load.h @@ -11,12 +11,14 @@ class ArgsManager; class CScheduler; -struct WalletContext; namespace interfaces { class Chain; } // namespace interfaces +namespace wallet { +struct WalletContext; + //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. bool VerifyWallets(WalletContext& context); @@ -34,5 +36,6 @@ void StopWallets(WalletContext& context); //! Close all wallets. void UnloadWallets(WalletContext& context); +} // namespace wallet #endif // BITCOIN_WALLET_LOAD_H diff --git a/src/wallet/receive.cpp b/src/wallet/receive.cpp index 2fb274b55f..1a6f06213c 100644 --- a/src/wallet/receive.cpp +++ b/src/wallet/receive.cpp @@ -8,6 +8,7 @@ #include <wallet/transaction.h> #include <wallet/wallet.h> +namespace wallet { isminetype InputIsMine(const CWallet& wallet, const CTxIn &txin) { AssertLockHeld(wallet.cs_wallet); @@ -278,8 +279,6 @@ bool CachedTxIsFromMe(const CWallet& wallet, const CWalletTx& wtx, const isminef bool CachedTxIsTrusted(const CWallet& wallet, const CWalletTx& wtx, std::set<uint256>& trusted_parents) { AssertLockHeld(wallet.cs_wallet); - // Quick answer in most cases - if (!wallet.chain().checkFinalTx(*wtx.tx)) return false; int nDepth = wallet.GetTxDepthInMainChain(wtx); if (nDepth >= 1) return true; if (nDepth < 0) return false; @@ -473,3 +472,4 @@ std::set< std::set<CTxDestination> > GetAddressGroupings(const CWallet& wallet) return ret; } +} // namespace wallet diff --git a/src/wallet/receive.h b/src/wallet/receive.h index f659955fc6..d7705b5262 100644 --- a/src/wallet/receive.h +++ b/src/wallet/receive.h @@ -10,6 +10,7 @@ #include <wallet/transaction.h> #include <wallet/wallet.h> +namespace wallet { isminetype InputIsMine(const CWallet& wallet, const CTxIn& txin) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); /** Returns whether all of the inputs match the filter */ @@ -60,5 +61,6 @@ Balance GetBalance(const CWallet& wallet, int min_depth = 0, bool avoid_reuse = std::map<CTxDestination, CAmount> GetAddressBalances(const CWallet& wallet); std::set<std::set<CTxDestination>> GetAddressGroupings(const CWallet& wallet) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet); +} // namespace wallet #endif // BITCOIN_WALLET_RECEIVE_H diff --git a/src/wallet/rpc/addresses.cpp b/src/wallet/rpc/addresses.cpp index 2db47bc855..51587a64a3 100644 --- a/src/wallet/rpc/addresses.cpp +++ b/src/wallet/rpc/addresses.cpp @@ -13,6 +13,7 @@ #include <univalue.h> +namespace wallet { RPCHelpMan getnewaddress() { return RPCHelpMan{"getnewaddress", @@ -802,3 +803,4 @@ RPCHelpMan walletdisplayaddress() }; } #endif // ENABLE_EXTERNAL_SIGNER +} // namespace wallet diff --git a/src/wallet/rpc/backup.cpp b/src/wallet/rpc/backup.cpp index 0029d1b09c..228564fae4 100644 --- a/src/wallet/rpc/backup.cpp +++ b/src/wallet/rpc/backup.cpp @@ -5,6 +5,7 @@ #include <chain.h> #include <clientversion.h> #include <core_io.h> +#include <fs.h> #include <interfaces/chain.h> #include <key_io.h> #include <merkleblock.h> @@ -20,8 +21,10 @@ #include <wallet/rpc/util.h> #include <wallet/wallet.h> -#include <stdint.h> +#include <cstdint> +#include <fstream> #include <tuple> +#include <string> #include <boost/algorithm/string.hpp> @@ -31,6 +34,7 @@ using interfaces::FoundBlock; +namespace wallet { std::string static EncodeDumpString(const std::string &str) { std::stringstream ret; for (const unsigned char c : str) { @@ -520,7 +524,7 @@ RPCHelpMan importwallet() EnsureWalletIsUnlocked(*pwallet); - fsbridge::ifstream file; + std::ifstream file; file.open(fs::u8path(request.params[0].get_str()), std::ios::in | std::ios::ate); if (!file.is_open()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -728,7 +732,7 @@ RPCHelpMan dumpwallet() throw JSONRPCError(RPC_INVALID_PARAMETER, filepath.u8string() + " already exists. If you are sure this is what you want, move it out of the way first"); } - fsbridge::ofstream file; + std::ofstream file; file.open(filepath); if (!file.is_open()) throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot open wallet dump file"); @@ -1887,7 +1891,7 @@ RPCHelpMan restorewallet() bilingual_str error; std::vector<bilingual_str> warnings; - const std::shared_ptr<CWallet> wallet = RestoreWallet(context, fs::PathToString(backup_file), wallet_name, load_on_start, status, error, warnings); + const std::shared_ptr<CWallet> wallet = RestoreWallet(context, backup_file, wallet_name, load_on_start, status, error, warnings); HandleWalletError(wallet, status, error); @@ -1900,3 +1904,4 @@ RPCHelpMan restorewallet() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/coins.cpp b/src/wallet/rpc/coins.cpp index f3294b4570..035541babd 100644 --- a/src/wallet/rpc/coins.cpp +++ b/src/wallet/rpc/coins.cpp @@ -15,6 +15,7 @@ #include <univalue.h> +namespace wallet { static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet) { std::set<CTxDestination> address_set; @@ -59,8 +60,8 @@ static CAmount GetReceived(const CWallet& wallet, const UniValue& params, bool b if (depth < min_depth // Coinbase with less than 1 confirmation is no longer in the main chain || (wtx.IsCoinBase() && (depth < 1 || !include_coinbase)) - || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase) - || !wallet.chain().checkFinalTx(*wtx.tx)) { + || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)) + { continue; } @@ -731,3 +732,4 @@ RPCHelpMan listunspent() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/encrypt.cpp b/src/wallet/rpc/encrypt.cpp index e659f434a3..2b6a2a198d 100644 --- a/src/wallet/rpc/encrypt.cpp +++ b/src/wallet/rpc/encrypt.cpp @@ -7,6 +7,7 @@ #include <wallet/wallet.h> +namespace wallet { RPCHelpMan walletpassphrase() { return RPCHelpMan{"walletpassphrase", @@ -246,3 +247,4 @@ RPCHelpMan encryptwallet() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/signmessage.cpp b/src/wallet/rpc/signmessage.cpp index bb8d7fc13f..438d290030 100644 --- a/src/wallet/rpc/signmessage.cpp +++ b/src/wallet/rpc/signmessage.cpp @@ -10,6 +10,7 @@ #include <univalue.h> +namespace wallet { RPCHelpMan signmessage() { return RPCHelpMan{"signmessage", @@ -66,3 +67,4 @@ RPCHelpMan signmessage() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/spend.cpp b/src/wallet/rpc/spend.cpp index 978174b340..433b5a1815 100644 --- a/src/wallet/rpc/spend.cpp +++ b/src/wallet/rpc/spend.cpp @@ -2,6 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <consensus/validation.h> #include <core_io.h> #include <key_io.h> #include <policy/policy.h> @@ -19,6 +20,7 @@ #include <univalue.h> +namespace wallet { static void ParseRecipients(const UniValue& address_amounts, const UniValue& subtract_fee_outputs, std::vector<CRecipient> &recipients) { std::set<CTxDestination> destinations; int i = 0; @@ -428,6 +430,7 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, {"replaceable", UniValueType(UniValue::VBOOL)}, {"conf_target", UniValueType(UniValue::VNUM)}, {"estimate_mode", UniValueType(UniValue::VSTR)}, + {"input_weights", UniValueType(UniValue::VARR)}, }, true, true); @@ -547,6 +550,37 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, } } + if (options.exists("input_weights")) { + for (const UniValue& input : options["input_weights"].get_array().getValues()) { + uint256 txid = ParseHashO(input, "txid"); + + const UniValue& vout_v = find_value(input, "vout"); + if (!vout_v.isNum()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); + } + int vout = vout_v.get_int(); + if (vout < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); + } + + const UniValue& weight_v = find_value(input, "weight"); + if (!weight_v.isNum()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing weight key"); + } + int64_t weight = weight_v.get_int64(); + const int64_t min_input_weight = GetTransactionInputWeight(CTxIn()); + CHECK_NONFATAL(min_input_weight == 165); + if (weight < min_input_weight) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, weight cannot be less than 165 (41 bytes (size of outpoint + sequence + empty scriptSig) * 4 (witness scaling factor)) + 1 (empty witness)"); + } + if (weight > MAX_STANDARD_TX_WEIGHT) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, weight cannot be greater than the maximum standard tx weight of %d", MAX_STANDARD_TX_WEIGHT)); + } + + coinControl.SetInputWeight(COutPoint(txid, vout), weight); + } + } + if (tx.vout.size() == 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); @@ -584,6 +618,23 @@ void FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& fee_out, } } +static void SetOptionsInputWeights(const UniValue& inputs, UniValue& options) +{ + if (options.exists("input_weights")) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Input weights should be specified in inputs rather than in options."); + } + if (inputs.size() == 0) { + return; + } + UniValue weights(UniValue::VARR); + for (const UniValue& input : inputs.getValues()) { + if (input.exists("weight")) { + weights.push_back(input); + } + } + options.pushKV("input_weights", weights); +} + RPCHelpMan fundrawtransaction() { return RPCHelpMan{"fundrawtransaction", @@ -625,6 +676,17 @@ RPCHelpMan fundrawtransaction() {"vout_index", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "The zero-based output index, before a change output is added."}, }, }, + {"input_weights", RPCArg::Type::ARR, RPCArg::Optional::OMITTED_NAMED_ARG, "Inputs and their corresponding weights", + { + {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, + {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output index"}, + {"weight", RPCArg::Type::NUM, RPCArg::Optional::NO, "The maximum weight for this input, " + "including the weight of the outpoint and sequence number. " + "Note that serialized signature sizes are not guaranteed to be consistent, " + "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures." + "Remember to convert serialized sizes to weight units when necessary."}, + }, + }, }, FundTxDoc()), "options"}, @@ -1006,6 +1068,11 @@ RPCHelpMan send() {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"}, + {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, " + "including the weight of the outpoint and sequence number. " + "Note that signature sizes are not guaranteed to be consistent, " + "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures." + "Remember to convert serialized sizes to weight units when necessary."}, }, }, {"locktime", RPCArg::Type::NUM, RPCArg::Default{0}, "Raw locktime. Non-0 value also locktime-activates inputs"}, @@ -1109,6 +1176,7 @@ RPCHelpMan send() // Automatically select coins, unless at least one is manually selected. Can // be overridden by options.add_inputs. coin_control.m_add_inputs = rawTx.vin.size() == 0; + SetOptionsInputWeights(options["inputs"], options); FundTransaction(*pwallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ false); bool add_to_wallet = true; @@ -1249,6 +1317,11 @@ RPCHelpMan walletcreatefundedpsbt() {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"}, {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"}, {"sequence", RPCArg::Type::NUM, RPCArg::DefaultHint{"depends on the value of the 'locktime' and 'options.replaceable' arguments"}, "The sequence number"}, + {"weight", RPCArg::Type::NUM, RPCArg::DefaultHint{"Calculated from wallet and solving data"}, "The maximum weight for this input, " + "including the weight of the outpoint and sequence number. " + "Note that signature sizes are not guaranteed to be consistent, " + "so the maximum DER signatures size of 73 bytes should be used when considering ECDSA signatures." + "Remember to convert serialized sizes to weight units when necessary."}, }, }, }, @@ -1329,10 +1402,12 @@ RPCHelpMan walletcreatefundedpsbt() }, true ); + UniValue options = request.params[3]; + CAmount fee; int change_position; bool rbf{wallet.m_signal_rbf}; - const UniValue &replaceable_arg = request.params[3]["replaceable"]; + const UniValue &replaceable_arg = options["replaceable"]; if (!replaceable_arg.isNull()) { RPCTypeCheckArgument(replaceable_arg, UniValue::VBOOL); rbf = replaceable_arg.isTrue(); @@ -1342,7 +1417,8 @@ RPCHelpMan walletcreatefundedpsbt() // Automatically select coins, unless at least one is manually selected. Can // be overridden by options.add_inputs. coin_control.m_add_inputs = rawTx.vin.size() == 0; - FundTransaction(wallet, rawTx, fee, change_position, request.params[3], coin_control, /* override_min_fee */ true); + SetOptionsInputWeights(request.params[0], options); + FundTransaction(wallet, rawTx, fee, change_position, options, coin_control, /* override_min_fee */ true); // Make a blank psbt PartiallySignedTransaction psbtx(rawTx); @@ -1367,3 +1443,4 @@ RPCHelpMan walletcreatefundedpsbt() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/transactions.cpp b/src/wallet/rpc/transactions.cpp index 8a1c0885ac..eef2c13ee1 100644 --- a/src/wallet/rpc/transactions.cpp +++ b/src/wallet/rpc/transactions.cpp @@ -13,6 +13,7 @@ using interfaces::FoundBlock; +namespace wallet { static void WalletTxToJSON(const CWallet& wallet, const CWalletTx& wtx, UniValue& entry) { interfaces::Chain& chain = wallet.chain(); @@ -113,8 +114,8 @@ static UniValue ListReceived(const CWallet& wallet, const UniValue& params, cons // Coinbase with less than 1 confirmation is no longer in the main chain if ((wtx.IsCoinBase() && (nDepth < 1 || !include_coinbase)) - || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase) - || !wallet.chain().checkFinalTx(*wtx.tx)) { + || (wallet.IsTxImmatureCoinBase(wtx) && !include_immature_coinbase)) + { continue; } @@ -958,3 +959,4 @@ RPCHelpMan abortrescan() }, }; } +} // namespace wallet diff --git a/src/wallet/rpc/util.cpp b/src/wallet/rpc/util.cpp index 9a40a67ee5..59683c5fd8 100644 --- a/src/wallet/rpc/util.cpp +++ b/src/wallet/rpc/util.cpp @@ -12,6 +12,7 @@ #include <univalue.h> +namespace wallet { static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"}; @@ -147,4 +148,5 @@ void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& st } throw JSONRPCError(code, error.original); } -}
\ No newline at end of file +} +} // namespace wallet diff --git a/src/wallet/rpc/util.h b/src/wallet/rpc/util.h index 5b00d2abcb..7b810eb06e 100644 --- a/src/wallet/rpc/util.h +++ b/src/wallet/rpc/util.h @@ -10,12 +10,14 @@ #include <string> #include <vector> +class JSONRPCRequest; +class UniValue; struct bilingual_str; + +namespace wallet { class CWallet; -enum class DatabaseStatus; -class JSONRPCRequest; class LegacyScriptPubKeyMan; -class UniValue; +enum class DatabaseStatus; struct WalletContext; extern const std::string HELP_REQUIRING_PASSPHRASE; @@ -39,5 +41,6 @@ bool ParseIncludeWatchonly(const UniValue& include_watchonly, const CWallet& wal std::string LabelFromValue(const UniValue& value); void HandleWalletError(const std::shared_ptr<CWallet> wallet, DatabaseStatus& status, bilingual_str& error); +} // namespace wallet #endif // BITCOIN_WALLET_RPC_UTIL_H diff --git a/src/wallet/rpc/wallet.cpp b/src/wallet/rpc/wallet.cpp index bbe40feec0..33ec715b51 100644 --- a/src/wallet/rpc/wallet.cpp +++ b/src/wallet/rpc/wallet.cpp @@ -18,6 +18,7 @@ #include <univalue.h> +namespace wallet { /** Checks if a CKey is in the given CWallet compressed or otherwise*/ bool HaveKey(const SigningProvider& wallet, const CKey& key) { @@ -729,3 +730,4 @@ static const CRPCCommand commands[] = // clang-format on return commands; } +} // namespace wallet diff --git a/src/wallet/rpc/wallet.h b/src/wallet/rpc/wallet.h index 537d9b2358..423fc892b2 100644 --- a/src/wallet/rpc/wallet.h +++ b/src/wallet/rpc/wallet.h @@ -9,6 +9,8 @@ class CRPCCommand; +namespace wallet { Span<const CRPCCommand> GetWalletRPCCommands(); +} // namespace wallet #endif // BITCOIN_WALLET_RPC_WALLET_H diff --git a/src/wallet/salvage.cpp b/src/wallet/salvage.cpp index 3859d67b6d..1ecc96fe0e 100644 --- a/src/wallet/salvage.cpp +++ b/src/wallet/salvage.cpp @@ -11,6 +11,7 @@ #include <wallet/wallet.h> #include <wallet/walletdb.h> +namespace wallet { /* End of headers, beginning of key/value data */ static const char *HEADER_END = "HEADER=END"; /* End of key/value data */ @@ -165,3 +166,4 @@ bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::v return fSuccess; } +} // namespace wallet diff --git a/src/wallet/salvage.h b/src/wallet/salvage.h index 5a8538f942..332aceb262 100644 --- a/src/wallet/salvage.h +++ b/src/wallet/salvage.h @@ -11,6 +11,8 @@ struct bilingual_str; +namespace wallet { bool RecoverDatabaseFile(const fs::path& file_path, bilingual_str& error, std::vector<bilingual_str>& warnings); +} // namespace wallet #endif // BITCOIN_WALLET_SALVAGE_H diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 04a52bc86f..7218ed11dc 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -17,6 +17,7 @@ #include <optional> +namespace wallet { //! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details. const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; @@ -2359,3 +2360,4 @@ bool DescriptorScriptPubKeyMan::CanUpdateToWalletDescriptor(const WalletDescript return true; } +} // namespace wallet diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h index 8f511b68b5..6eda133771 100644 --- a/src/wallet/scriptpubkeyman.h +++ b/src/wallet/scriptpubkeyman.h @@ -25,6 +25,7 @@ enum class OutputType; struct bilingual_str; +namespace wallet { // Wallet storage things that ScriptPubKeyMans need in order to be able to store things to the wallet database. // It provides access to things that are part of the entire wallet and not specific to a ScriptPubKeyMan such as // wallet flags, wallet version, encryption keys, encryption status, and the database itself. This allows a @@ -631,5 +632,6 @@ public: void UpgradeDescriptorCache(); }; +} // namespace wallet #endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp index 5d9cc7bf6b..3d8ae2da69 100644 --- a/src/wallet/spend.cpp +++ b/src/wallet/spend.cpp @@ -21,6 +21,7 @@ using interfaces::FoundBlock; +namespace wallet { static constexpr size_t OUTPUT_GROUP_MAX_ENTRIES{100}; int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig) @@ -104,10 +105,6 @@ void AvailableCoins(const CWallet& wallet, std::vector<COutput>& vCoins, const C const uint256& wtxid = entry.first; const CWalletTx& wtx = entry.second; - if (!wallet.chain().checkFinalTx(*wtx.tx)) { - continue; - } - if (wallet.IsTxImmatureCoinBase(wtx)) continue; @@ -454,15 +451,17 @@ std::optional<SelectionResult> SelectCoins(const CWallet& wallet, const std::vec } input_bytes = GetTxSpendSize(wallet, wtx, outpoint.n, false); txout = wtx.tx->vout.at(outpoint.n); - } - if (input_bytes == -1) { - // The input is external. We either did not find the tx in mapWallet, or we did but couldn't compute the input size with wallet data + } else { + // The input is external. We did not find the tx in mapWallet. if (!coin_control.GetExternalOutput(outpoint, txout)) { - // Not ours, and we don't have solving data. return std::nullopt; } input_bytes = CalculateMaximumSignedInputSize(txout, &coin_control.m_external_provider, /* use_max_sig */ true); } + // If available, override calculated size with coin control specified size + if (coin_control.HasInputWeight(outpoint)) { + input_bytes = GetVirtualTransactionSize(coin_control.GetInputWeight(outpoint), 0, 0); + } CInputCoin coin(outpoint, txout, input_bytes); if (coin.m_input_bytes == -1) { @@ -665,8 +664,6 @@ static bool CreateTransactionInternal( } // Create change script that will be used if we need change - // TODO: pass in scriptChange instead of reservedest so - // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; // coin control: send change to custom address @@ -799,7 +796,7 @@ static bool CreateTransactionInternal( // to avoid conflicting with other possible uses of nSequence, // and in the spirit of "smallest possible change from prior // behavior." - const uint32_t nSequence = coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : (CTxIn::SEQUENCE_FINAL - 1); + const uint32_t nSequence{coin_control.m_signal_bip125_rbf.value_or(wallet.m_signal_rbf) ? MAX_BIP125_RBF_SEQUENCE : CTxIn::MAX_SEQUENCE_NONFINAL}; for (const auto& coin : selected_coins) { txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence)); } @@ -1030,3 +1027,4 @@ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, return true; } +} // namespace wallet diff --git a/src/wallet/spend.h b/src/wallet/spend.h index 268bcfc033..4453fb2762 100644 --- a/src/wallet/spend.h +++ b/src/wallet/spend.h @@ -10,6 +10,7 @@ #include <wallet/transaction.h> #include <wallet/wallet.h> +namespace wallet { /** Get the marginal bytes if spending the specified output from this transaction */ int GetTxSpendSize(const CWallet& wallet, const CWalletTx& wtx, unsigned int out, bool use_max_sig = false); @@ -142,5 +143,6 @@ bool CreateTransaction(CWallet& wallet, const std::vector<CRecipient>& vecSend, * calling CreateTransaction(); */ bool FundTransaction(CWallet& wallet, CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, bilingual_str& error, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl); +} // namespace wallet #endif // BITCOIN_WALLET_SPEND_H diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 3bea0de99f..2b2181e70b 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -20,6 +20,7 @@ #include <utility> #include <vector> +namespace wallet { static constexpr int32_t WALLET_SCHEMA_VERSION = 0; static Mutex g_sqlite_mutex; @@ -394,9 +395,9 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value) return false; } // Leftmost column in result is index 0 - const char* data = reinterpret_cast<const char*>(sqlite3_column_blob(m_read_stmt, 0)); - int data_size = sqlite3_column_bytes(m_read_stmt, 0); - value.write(data, data_size); + const std::byte* data{BytePtr(sqlite3_column_blob(m_read_stmt, 0))}; + size_t data_size(sqlite3_column_bytes(m_read_stmt, 0)); + value.write({data, data_size}); sqlite3_clear_bindings(m_read_stmt); sqlite3_reset(m_read_stmt); @@ -511,12 +512,12 @@ bool SQLiteBatch::ReadAtCursor(CDataStream& key, CDataStream& value, bool& compl } // Leftmost column in result is index 0 - const char* key_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 0)); - int key_data_size = sqlite3_column_bytes(m_cursor_stmt, 0); - key.write(key_data, key_data_size); - const char* value_data = reinterpret_cast<const char*>(sqlite3_column_blob(m_cursor_stmt, 1)); - int value_data_size = sqlite3_column_bytes(m_cursor_stmt, 1); - value.write(value_data, value_data_size); + const std::byte* key_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 0))}; + size_t key_data_size(sqlite3_column_bytes(m_cursor_stmt, 0)); + key.write({key_data, key_data_size}); + const std::byte* value_data{BytePtr(sqlite3_column_blob(m_cursor_stmt, 1))}; + size_t value_data_size(sqlite3_column_bytes(m_cursor_stmt, 1)); + value.write({value_data, value_data_size}); return true; } @@ -578,3 +579,4 @@ std::string SQLiteDatabaseVersion() { return std::string(sqlite3_libversion()); } +} // namespace wallet diff --git a/src/wallet/sqlite.h b/src/wallet/sqlite.h index 70ab4f797a..3ed598d0d2 100644 --- a/src/wallet/sqlite.h +++ b/src/wallet/sqlite.h @@ -10,6 +10,8 @@ #include <sqlite3.h> struct bilingual_str; + +namespace wallet { class SQLiteDatabase; /** RAII class that provides access to a WalletDatabase */ @@ -116,5 +118,6 @@ public: std::unique_ptr<SQLiteDatabase> MakeSQLiteDatabase(const fs::path& path, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); std::string SQLiteDatabaseVersion(); +} // namespace wallet #endif // BITCOIN_WALLET_SQLITE_H diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 9ab3c81078..b9f12158ca 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -18,6 +18,7 @@ #include <boost/test/unit_test.hpp> #include <random> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup) // how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles @@ -807,3 +808,4 @@ BOOST_AUTO_TEST_CASE(waste_test) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/db_tests.cpp b/src/wallet/test/db_tests.cpp index d9359bc171..35ae3707f8 100644 --- a/src/wallet/test/db_tests.cpp +++ b/src/wallet/test/db_tests.cpp @@ -2,15 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include <memory> - #include <boost/test/unit_test.hpp> #include <fs.h> #include <test/util/setup_common.h> #include <wallet/bdb.h> +#include <fstream> +#include <memory> +#include <string> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(db_tests, BasicTestingSetup) static std::shared_ptr<BerkeleyEnvironment> GetWalletEnv(const fs::path& path, std::string& database_filename) @@ -25,7 +27,7 @@ BOOST_AUTO_TEST_CASE(getwalletenv_file) std::string test_name = "test_name.dat"; const fs::path datadir = gArgs.GetDataDirNet(); fs::path file_path = datadir / test_name; - fs::ofstream f(file_path); + std::ofstream f{file_path}; f.close(); std::string filename; @@ -77,3 +79,4 @@ BOOST_AUTO_TEST_CASE(getwalletenv_g_dbenvs_free_instance) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/fuzz/notifications.cpp b/src/wallet/test/fuzz/notifications.cpp index 0601c492cd..1c16da25bd 100644 --- a/src/wallet/test/fuzz/notifications.cpp +++ b/src/wallet/test/fuzz/notifications.cpp @@ -18,6 +18,7 @@ #include <string> #include <vector> +namespace wallet { namespace { const TestingSetup* g_setup; @@ -168,3 +169,4 @@ FUZZ_TARGET_INIT(wallet_notifications, initialize_setup) } } } // namespace +} // namespace wallet diff --git a/src/wallet/test/init_test_fixture.cpp b/src/wallet/test/init_test_fixture.cpp index 439489ab59..be38cebafd 100644 --- a/src/wallet/test/init_test_fixture.cpp +++ b/src/wallet/test/init_test_fixture.cpp @@ -7,8 +7,12 @@ #include <util/check.h> #include <util/system.h> +#include <fstream> +#include <string> + #include <wallet/test/init_test_fixture.h> +namespace wallet { InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainName) : BasicTestingSetup(chainName) { m_wallet_loader = MakeWalletLoader(*m_node.chain, *Assert(m_node.args)); @@ -23,8 +27,8 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam m_walletdir_path_cases["custom"] = m_datadir / "my_wallets"; m_walletdir_path_cases["nonexistent"] = m_datadir / "path_does_not_exist"; m_walletdir_path_cases["file"] = m_datadir / "not_a_directory.dat"; - m_walletdir_path_cases["trailing"] = m_datadir / "wallets" / sep; - m_walletdir_path_cases["trailing2"] = m_datadir / "wallets" / sep / sep; + m_walletdir_path_cases["trailing"] = m_datadir / ("wallets" + sep); + m_walletdir_path_cases["trailing2"] = m_datadir / ("wallets" + sep + sep); fs::current_path(m_datadir); m_walletdir_path_cases["relative"] = "wallets"; @@ -32,7 +36,7 @@ InitWalletDirTestingSetup::InitWalletDirTestingSetup(const std::string& chainNam fs::create_directories(m_walletdir_path_cases["default"]); fs::create_directories(m_walletdir_path_cases["custom"]); fs::create_directories(m_walletdir_path_cases["relative"]); - fs::ofstream f(m_walletdir_path_cases["file"]); + std::ofstream f{m_walletdir_path_cases["file"]}; f.close(); } @@ -48,3 +52,4 @@ void InitWalletDirTestingSetup::SetWalletDir(const fs::path& walletdir_path) { gArgs.ForceSetArg("-walletdir", fs::PathToString(walletdir_path)); } +} // namespace wallet diff --git a/src/wallet/test/init_test_fixture.h b/src/wallet/test/init_test_fixture.h index ccad629543..df5819fd1d 100644 --- a/src/wallet/test/init_test_fixture.h +++ b/src/wallet/test/init_test_fixture.h @@ -11,6 +11,7 @@ #include <test/util/setup_common.h> +namespace wallet { struct InitWalletDirTestingSetup: public BasicTestingSetup { explicit InitWalletDirTestingSetup(const std::string& chainName = CBaseChainParams::MAIN); ~InitWalletDirTestingSetup(); @@ -23,3 +24,4 @@ struct InitWalletDirTestingSetup: public BasicTestingSetup { }; #endif // BITCOIN_WALLET_TEST_INIT_TEST_FIXTURE_H +} // namespace wallet diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp index 439b17fe13..d1df48a312 100644 --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -10,6 +10,7 @@ #include <util/system.h> #include <wallet/test/init_test_fixture.h> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(init_tests, InitWalletDirTestingSetup) BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) @@ -72,6 +73,8 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) BOOST_CHECK_EQUAL(walletdir, expected_path); } +#ifndef WIN32 +// Windows does not consider "datadir/wallets//" to be a valid directory path. BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) { SetWalletDir(m_walletdir_path_cases["trailing2"]); @@ -81,5 +84,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } +#endif BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp index a9b3f1cbfd..dd5cd0af46 100644 --- a/src/wallet/test/ismine_tests.cpp +++ b/src/wallet/test/ismine_tests.cpp @@ -13,6 +13,7 @@ #include <boost/test/unit_test.hpp> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(ismine_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(ismine_standard) @@ -417,3 +418,4 @@ BOOST_AUTO_TEST_CASE(ismine_standard) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index 4c3c23599e..b953f402a2 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -11,6 +11,7 @@ #include <test/util/setup_common.h> #include <wallet/test/wallet_test_fixture.h> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(psbt_wallet_tests, WalletTestingSetup) static void import_descriptor(CWallet& wallet, const std::string& descriptor) @@ -145,3 +146,4 @@ BOOST_AUTO_TEST_CASE(parse_hd_keypath) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/scriptpubkeyman_tests.cpp b/src/wallet/test/scriptpubkeyman_tests.cpp index f51648ec3b..a524b85ccb 100644 --- a/src/wallet/test/scriptpubkeyman_tests.cpp +++ b/src/wallet/test/scriptpubkeyman_tests.cpp @@ -10,6 +10,7 @@ #include <boost/test/unit_test.hpp> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(scriptpubkeyman_tests, BasicTestingSetup) // Test LegacyScriptPubKeyMan::CanProvide behavior, making sure it returns true @@ -39,3 +40,4 @@ BOOST_AUTO_TEST_CASE(CanProvide) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/spend_tests.cpp b/src/wallet/test/spend_tests.cpp index 926f28686d..334bd5b8bc 100644 --- a/src/wallet/test/spend_tests.cpp +++ b/src/wallet/test/spend_tests.cpp @@ -12,6 +12,7 @@ #include <boost/test/unit_test.hpp> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(spend_tests, WalletTestingSetup) BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup) @@ -62,4 +63,56 @@ BOOST_FIXTURE_TEST_CASE(SubtractFee, TestChain100Setup) BOOST_CHECK_EQUAL(fee, check_tx(fee + 123)); } +static void TestFillInputToWeight(int64_t additional_weight, std::vector<int64_t> expected_stack_sizes) +{ + static const int64_t EMPTY_INPUT_WEIGHT = GetTransactionInputWeight(CTxIn()); + + CTxIn input; + int64_t target_weight = EMPTY_INPUT_WEIGHT + additional_weight; + BOOST_CHECK(FillInputToWeight(input, target_weight)); + BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), target_weight); + BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), expected_stack_sizes.size()); + for (unsigned int i = 0; i < expected_stack_sizes.size(); ++i) { + BOOST_CHECK_EQUAL(input.scriptWitness.stack[i].size(), expected_stack_sizes[i]); + } +} + +BOOST_FIXTURE_TEST_CASE(FillInputToWeightTest, BasicTestingSetup) +{ + { + // Less than or equal minimum of 165 should not add any witness data + CTxIn input; + BOOST_CHECK(!FillInputToWeight(input, -1)); + BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165); + BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0); + BOOST_CHECK(!FillInputToWeight(input, 0)); + BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165); + BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0); + BOOST_CHECK(!FillInputToWeight(input, 164)); + BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165); + BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0); + BOOST_CHECK(FillInputToWeight(input, 165)); + BOOST_CHECK_EQUAL(GetTransactionInputWeight(input), 165); + BOOST_CHECK_EQUAL(input.scriptWitness.stack.size(), 0); + } + + // Make sure we can add at least one weight + TestFillInputToWeight(1, {0}); + + // 1 byte compact size uint boundary + TestFillInputToWeight(252, {251}); + TestFillInputToWeight(253, {83, 168}); + TestFillInputToWeight(262, {86, 174}); + TestFillInputToWeight(263, {260}); + + // 3 byte compact size uint boundary + TestFillInputToWeight(65535, {65532}); + TestFillInputToWeight(65536, {21842, 43688}); + TestFillInputToWeight(65545, {21845, 43694}); + TestFillInputToWeight(65546, {65541}); + + // Note: We don't test the next boundary because of memory allocation constraints. +} + BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/util.cpp b/src/wallet/test/util.cpp index 93a3404d2c..aa3121511d 100644 --- a/src/wallet/test/util.cpp +++ b/src/wallet/test/util.cpp @@ -15,6 +15,7 @@ #include <memory> +namespace wallet { std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key) { auto wallet = std::make_unique<CWallet>(&chain, "", args, CreateMockWalletDatabase()); @@ -44,3 +45,4 @@ std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cc BOOST_CHECK(result.last_failed_block.IsNull()); return wallet; } +} // namespace wallet diff --git a/src/wallet/test/util.h b/src/wallet/test/util.h index 3adb82b85f..712d0251cd 100644 --- a/src/wallet/test/util.h +++ b/src/wallet/test/util.h @@ -10,11 +10,14 @@ class ArgsManager; class CChain; class CKey; -class CWallet; namespace interfaces { class Chain; } // namespace interfaces +namespace wallet { +class CWallet; + std::unique_ptr<CWallet> CreateSyncedWallet(interfaces::Chain& chain, CChain& cchain, ArgsManager& args, const CKey& key); +} // namespace wallet #endif // BITCOIN_WALLET_TEST_UTIL_H diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp index 5b421840e0..166e27bab9 100644 --- a/src/wallet/test/wallet_crypto_tests.cpp +++ b/src/wallet/test/wallet_crypto_tests.cpp @@ -10,6 +10,7 @@ #include <boost/test/unit_test.hpp> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(wallet_crypto_tests, BasicTestingSetup) class TestCrypter @@ -124,3 +125,4 @@ BOOST_AUTO_TEST_CASE(decrypt) { } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/wallet_test_fixture.cpp b/src/wallet/test/wallet_test_fixture.cpp index ad24662fb6..cb006dea3a 100644 --- a/src/wallet/test/wallet_test_fixture.cpp +++ b/src/wallet/test/wallet_test_fixture.cpp @@ -6,6 +6,7 @@ #include <scheduler.h> +namespace wallet { WalletTestingSetup::WalletTestingSetup(const std::string& chainName) : TestingSetup(chainName), m_wallet(m_node.chain.get(), "", m_args, CreateMockWalletDatabase()) @@ -19,3 +20,4 @@ WalletTestingSetup::~WalletTestingSetup() { if (m_node.scheduler) m_node.scheduler->stop(); } +} // namespace wallet diff --git a/src/wallet/test/wallet_test_fixture.h b/src/wallet/test/wallet_test_fixture.h index cb6a8402dd..d4b855b145 100644 --- a/src/wallet/test/wallet_test_fixture.h +++ b/src/wallet/test/wallet_test_fixture.h @@ -15,6 +15,7 @@ #include <memory> +namespace wallet { /** Testing setup and teardown for wallet. */ struct WalletTestingSetup : public TestingSetup { @@ -25,5 +26,6 @@ struct WalletTestingSetup : public TestingSetup { CWallet m_wallet; std::unique_ptr<interfaces::Handler> m_chain_notifications_handler; }; +} // namespace wallet #endif // BITCOIN_WALLET_TEST_WALLET_TEST_FIXTURE_H diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 48998594f1..8ef0d46c4f 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -30,6 +30,10 @@ #include <boost/test/unit_test.hpp> #include <univalue.h> +using node::MAX_BLOCKFILE_SIZE; +using node::UnlinkPrunedFiles; + +namespace wallet { RPCHelpMan importmulti(); RPCHelpMan dumpwallet(); RPCHelpMan importwallet(); @@ -92,7 +96,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) { // Cap last block file size, and mine new block in a new block file. CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip(); - GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; + WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip(); @@ -136,11 +140,13 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) } // Prune the older block file. + int file_number; { LOCK(cs_main); - Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile); + file_number = oldTip->GetBlockPos().nFile; + Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number); } - UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); + UnlinkPrunedFiles({file_number}); // Verify ScanForWalletTransactions only picks transactions in the new block // file. @@ -165,9 +171,10 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) // Prune the remaining block file. { LOCK(cs_main); - Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile); + file_number = newTip->GetBlockPos().nFile; + Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number); } - UnlinkPrunedFiles({newTip->GetBlockPos().nFile}); + UnlinkPrunedFiles({file_number}); // Verify ScanForWalletTransactions scans no blocks. { @@ -193,16 +200,18 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup) { // Cap last block file size, and mine new block in a new block file. CBlockIndex* oldTip = m_node.chainman->ActiveChain().Tip(); - GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE; + WITH_LOCK(::cs_main, m_node.chainman->m_blockman.GetBlockFileInfo(oldTip->GetBlockPos().nFile)->nSize = MAX_BLOCKFILE_SIZE); CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); CBlockIndex* newTip = m_node.chainman->ActiveChain().Tip(); // Prune the older block file. + int file_number; { LOCK(cs_main); - Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile); + file_number = oldTip->GetBlockPos().nFile; + Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(file_number); } - UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); + UnlinkPrunedFiles({file_number}); // Verify importmulti RPC returns failure for a key whose creation time is // before the missing block, and success for a key whose creation time is @@ -289,7 +298,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) request.params.setArray(); request.params.push_back(backup_file); - ::dumpwallet().HandleRequest(request); + wallet::dumpwallet().HandleRequest(request); RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt); } @@ -308,7 +317,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) request.params.push_back(backup_file); AddWallet(context, wallet); wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash()); - ::importwallet().HandleRequest(request); + wallet::importwallet().HandleRequest(request); RemoveWallet(context, wallet, /* load_on_start= */ std::nullopt); BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U); @@ -816,38 +825,34 @@ BOOST_FIXTURE_TEST_CASE(ZapSelectTx, TestChain100Setup) context.args = &gArgs; context.chain = m_node.chain.get(); auto wallet = TestLoadWallet(context); - AddKey(*wallet, coinbaseKey); + CKey key; + key.MakeNewKey(true); + AddKey(*wallet, key); - // rescan to ensure coinbase transactions from test fixture are picked up by the wallet - { - WalletRescanReserver reserver(*wallet); - reserver.reserve(); - wallet->ScanForWalletTransactions(m_node.chain->getBlockHash(0), 0, /* max height= */ {}, reserver, /* update= */ true); - } - // create one more block to get the first block coinbase to maturity + std::string error; m_coinbase_txns.push_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); - // spend first coinbase tx - auto spend_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); - CreateAndProcessBlock({spend_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); + auto block_tx = TestSimpleSpend(*m_coinbase_txns[0], 0, coinbaseKey, GetScriptForRawPubKey(key.GetPubKey())); + CreateAndProcessBlock({block_tx}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())); SyncWithValidationInterfaceQueue(); { - auto spend_tx_hash = spend_tx.GetHash(); + auto block_hash = block_tx.GetHash(); auto prev_hash = m_coinbase_txns[0]->GetHash(); LOCK(wallet->cs_wallet); BOOST_CHECK(wallet->HasWalletSpend(prev_hash)); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 1u); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 1u); - std::vector<uint256> vHashIn{spend_tx_hash}, vHashOut; + std::vector<uint256> vHashIn{ block_hash }, vHashOut; BOOST_CHECK_EQUAL(wallet->ZapSelectTx(vHashIn, vHashOut), DBErrors::LOAD_OK); BOOST_CHECK(!wallet->HasWalletSpend(prev_hash)); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(spend_tx_hash), 0u); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_hash), 0u); } TestUnloadWallet(std::move(wallet)); } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/wallet_transaction_tests.cpp b/src/wallet/test/wallet_transaction_tests.cpp index 5ef2904f66..9f56248614 100644 --- a/src/wallet/test/wallet_transaction_tests.cpp +++ b/src/wallet/test/wallet_transaction_tests.cpp @@ -8,6 +8,7 @@ #include <boost/test/unit_test.hpp> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(wallet_transaction_tests, WalletTestingSetup) BOOST_AUTO_TEST_CASE(roundtrip) @@ -22,3 +23,4 @@ BOOST_AUTO_TEST_CASE(roundtrip) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/test/walletdb_tests.cpp b/src/wallet/test/walletdb_tests.cpp index 558121ae42..e251a3a0e4 100644 --- a/src/wallet/test/walletdb_tests.cpp +++ b/src/wallet/test/walletdb_tests.cpp @@ -9,6 +9,7 @@ #include <boost/test/unit_test.hpp> +namespace wallet { BOOST_FIXTURE_TEST_SUITE(walletdb_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue) @@ -27,3 +28,4 @@ BOOST_AUTO_TEST_CASE(walletdb_readkeyvalue) } BOOST_AUTO_TEST_SUITE_END() +} // namespace wallet diff --git a/src/wallet/transaction.cpp b/src/wallet/transaction.cpp index a926c0ecc1..a46846c1d4 100644 --- a/src/wallet/transaction.cpp +++ b/src/wallet/transaction.cpp @@ -4,6 +4,7 @@ #include <wallet/transaction.h> +namespace wallet { bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const { CMutableTransaction tx1 {*this->tx}; @@ -23,3 +24,4 @@ int64_t CWalletTx::GetTxTime() const int64_t n = nTimeSmart; return n ? n : nTimeReceived; } +} // namespace wallet diff --git a/src/wallet/transaction.h b/src/wallet/transaction.h index 52d72cccf3..00f9c9f154 100644 --- a/src/wallet/transaction.h +++ b/src/wallet/transaction.h @@ -19,6 +19,7 @@ #include <variant> #include <vector> +namespace wallet { //! State of transaction confirmed in a block. struct TxStateConfirmed { uint256 confirmed_block_hash; @@ -303,5 +304,6 @@ public: CWalletTx(CWalletTx const &) = delete; void operator=(CWalletTx const &x) = delete; }; +} // namespace wallet #endif // BITCOIN_WALLET_TRANSACTION_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 267710e8c7..7e694d1987 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -48,6 +48,7 @@ using interfaces::FoundBlock; +namespace wallet { const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS{ {WALLET_FLAG_AVOID_REUSE, "You need to rescan the blockchain in order to correctly mark used " @@ -357,12 +358,12 @@ std::shared_ptr<CWallet> CreateWallet(WalletContext& context, const std::string& return wallet; } -std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const std::string& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) +std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings) { DatabaseOptions options; options.require_existing = true; - if (!fs::exists(fs::u8path(backup_file))) { + if (!fs::exists(backup_file)) { error = Untranslated("Backup file does not exist"); status = DatabaseStatus::FAILED_INVALID_BACKUP_FILE; return nullptr; @@ -377,7 +378,7 @@ std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const std::string } auto wallet_file = wallet_path / "wallet.dat"; - fs::copy_file(backup_file, wallet_file, fs::copy_option::fail_if_exists); + fs::copy_file(backup_file, wallet_file, fs::copy_options::none); auto wallet = LoadWallet(context, wallet_name, load_on_start, options, status, error, warnings); @@ -951,9 +952,7 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const TxState& state, const wtx.nOrderPos = IncOrderPosNext(&batch); wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); wtx.nTimeSmart = ComputeTimeSmart(wtx, rescanning_old_block); - if (IsFromMe(*tx.get())) { - AddToSpends(hash); - } + AddToSpends(hash, &batch); } if (!fInsertedNew) @@ -1506,6 +1505,49 @@ bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut return true; } +bool FillInputToWeight(CTxIn& txin, int64_t target_weight) +{ + assert(txin.scriptSig.empty()); + assert(txin.scriptWitness.IsNull()); + + int64_t txin_weight = GetTransactionInputWeight(txin); + + // Do nothing if the weight that should be added is less than the weight that already exists + if (target_weight < txin_weight) { + return false; + } + if (target_weight == txin_weight) { + return true; + } + + // Subtract current txin weight, which should include empty witness stack + int64_t add_weight = target_weight - txin_weight; + assert(add_weight > 0); + + // We will want to subtract the size of the Compact Size UInt that will also be serialized. + // However doing so when the size is near a boundary can result in a problem where it is not + // possible to have a stack element size and combination to exactly equal a target. + // To avoid this possibility, if the weight to add is less than 10 bytes greater than + // a boundary, the size will be split so that 2/3rds will be in one stack element, and + // the remaining 1/3rd in another. Using 3rds allows us to avoid additional boundaries. + // 10 bytes is used because that accounts for the maximum size. This does not need to be super precise. + if ((add_weight >= 253 && add_weight < 263) + || (add_weight > std::numeric_limits<uint16_t>::max() && add_weight <= std::numeric_limits<uint16_t>::max() + 10) + || (add_weight > std::numeric_limits<uint32_t>::max() && add_weight <= std::numeric_limits<uint32_t>::max() + 10)) { + int64_t first_weight = add_weight / 3; + add_weight -= first_weight; + + first_weight -= GetSizeOfCompactSize(first_weight); + txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), first_weight, 0); + } + + add_weight -= GetSizeOfCompactSize(add_weight); + txin.scriptWitness.stack.emplace(txin.scriptWitness.stack.end(), add_weight, 0); + assert(GetTransactionInputWeight(txin) == target_weight); + + return true; +} + // Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 bytes) bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> &txouts, const CCoinControl* coin_control) const { @@ -1514,6 +1556,14 @@ bool CWallet::DummySignTx(CMutableTransaction &txNew, const std::vector<CTxOut> for (const auto& txout : txouts) { CTxIn& txin = txNew.vin[nIn]; + // If weight was provided, fill the input to that weight + if (coin_control && coin_control->HasInputWeight(txin.prevout)) { + if (!FillInputToWeight(txin, coin_control->GetInputWeight(txin.prevout))) { + return false; + } + nIn++; + continue; + } // Use max sig if watch only inputs were used or if this particular input is an external input // to ensure a sufficient fee is attained for the requested feerate. const bool use_max_sig = coin_control && (coin_control->fAllowWatchOnly || coin_control->IsExternalSelected(txin.prevout)); @@ -2601,9 +2651,9 @@ std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, cons // 4. For backwards compatibility, the name of a data file in -walletdir. const fs::path wallet_path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name)); fs::file_type path_type = fs::symlink_status(wallet_path).type(); - if (!(path_type == fs::file_not_found || path_type == fs::directory_file || - (path_type == fs::symlink_file && fs::is_directory(wallet_path)) || - (path_type == fs::regular_file && fs::PathFromString(name).filename() == fs::PathFromString(name)))) { + if (!(path_type == fs::file_type::not_found || path_type == fs::file_type::directory || + (path_type == fs::file_type::symlink && fs::is_directory(wallet_path)) || + (path_type == fs::file_type::regular && fs::PathFromString(name).filename() == fs::PathFromString(name)))) { error_string = Untranslated(strprintf( "Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " "database/log.?????????? files can be stored, a location where such a directory could be created, " @@ -3433,3 +3483,4 @@ ScriptPubKeyMan* CWallet::AddWalletDescriptor(WalletDescriptor& desc, const Flat return spk_man; } +} // namespace wallet diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index cbcdcaf3b8..e2c5c69c91 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -7,6 +7,7 @@ #define BITCOIN_WALLET_WALLET_H #include <consensus/amount.h> +#include <fs.h> #include <interfaces/chain.h> #include <interfaces/handler.h> #include <outputtype.h> @@ -40,12 +41,17 @@ #include <boost/signals2/signal.hpp> -struct WalletContext; using LoadWalletFn = std::function<void(std::unique_ptr<interfaces::Wallet> wallet)>; +class CScript; +enum class FeeEstimateMode; +struct FeeCalculation; struct bilingual_str; +namespace wallet { +struct WalletContext; + //! Explicitly unload and delete the wallet. //! Blocks the current thread after signaling the unload intent so that all //! wallet pointer owners release the wallet. @@ -60,7 +66,7 @@ std::vector<std::shared_ptr<CWallet>> GetWallets(WalletContext& context); std::shared_ptr<CWallet> GetWallet(WalletContext& context, const std::string& name); std::shared_ptr<CWallet> LoadWallet(WalletContext& context, 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(WalletContext& context, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); -std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const std::string& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); +std::shared_ptr<CWallet> RestoreWallet(WalletContext& context, const fs::path& backup_file, const std::string& wallet_name, std::optional<bool> load_on_start, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings); std::unique_ptr<interfaces::Handler> HandleLoadWallet(WalletContext& context, LoadWalletFn load_wallet); std::unique_ptr<WalletDatabase> MakeWalletDatabase(const std::string& name, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error); @@ -107,10 +113,7 @@ static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91; class CCoinControl; class COutput; -class CScript; class CWalletTx; -struct FeeCalculation; -enum class FeeEstimateMode; class ReserveDestination; //! Default for -addresstype @@ -937,4 +940,7 @@ bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_nam bool DummySignInput(const SigningProvider& provider, CTxIn &tx_in, const CTxOut &txout, bool use_max_sig); +bool FillInputToWeight(CTxIn& txin, int64_t target_weight); +} // namespace wallet + #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4b51d60597..c11d4b562d 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -26,6 +26,7 @@ #include <optional> #include <string> +namespace wallet { namespace DBKeys { const std::string ACENTRY{"acentry"}; const std::string ACTIVEEXTERNALSPK{"activeexternalspk"}; @@ -1104,7 +1105,7 @@ std::unique_ptr<WalletDatabase> MakeDatabase(const fs::path& path, const Databas { bool exists; try { - exists = fs::symlink_status(path).type() != fs::file_not_found; + exists = fs::symlink_status(path).type() != fs::file_type::not_found; } catch (const fs::filesystem_error& e) { error = Untranslated(strprintf("Failed to access database path '%s': %s", fs::PathToString(path), fsbridge::get_filesystem_error_message(e))); status = DatabaseStatus::FAILED_BAD_PATH; @@ -1194,3 +1195,4 @@ std::unique_ptr<WalletDatabase> CreateMockWalletDatabase() return std::make_unique<BerkeleyDatabase>(std::make_shared<BerkeleyEnvironment>(), ""); #endif } +} // namespace wallet diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index be77fb03a2..7d38832aa5 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -15,6 +15,18 @@ #include <string> #include <vector> +class CScript; +class uint160; +class uint256; +struct CBlockLocator; + +namespace wallet { +class CKeyPool; +class CMasterKey; +class CWallet; +class CWalletTx; +struct WalletContext; + /** * Overview of wallet database classes: * @@ -29,16 +41,6 @@ static const bool DEFAULT_FLUSHWALLET = true; -struct CBlockLocator; -struct WalletContext; -class CKeyPool; -class CMasterKey; -class CScript; -class CWallet; -class CWalletTx; -class uint160; -class uint256; - /** Error statuses for the wallet database */ enum class DBErrors { @@ -297,5 +299,6 @@ std::unique_ptr<WalletDatabase> CreateDummyWalletDatabase(); /** Return object for accessing temporary in-memory database. */ std::unique_ptr<WalletDatabase> CreateMockWalletDatabase(); +} // namespace wallet #endif // BITCOIN_WALLET_WALLETDB_H diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp index 900651939c..9cd18dd0a5 100644 --- a/src/wallet/wallettool.cpp +++ b/src/wallet/wallettool.cpp @@ -16,6 +16,7 @@ #include <wallet/wallet.h> #include <wallet/walletutil.h> +namespace wallet { namespace WalletTool { // The standard wallet deleter function blocks on the validation interface @@ -219,3 +220,4 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command) return true; } } // namespace WalletTool +} // namespace wallet diff --git a/src/wallet/wallettool.h b/src/wallet/wallettool.h index cdd728db33..9e0fe2b0ec 100644 --- a/src/wallet/wallettool.h +++ b/src/wallet/wallettool.h @@ -9,10 +9,12 @@ class ArgsManager; +namespace wallet { namespace WalletTool { bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command); } // namespace WalletTool +} // namespace wallet #endif // BITCOIN_WALLET_WALLETTOOL_H diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index d32fdb1fa8..ce276451c3 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -7,6 +7,7 @@ #include <logging.h> #include <util/system.h> +namespace wallet { fs::path GetWalletDir() { fs::path path; @@ -42,3 +43,4 @@ WalletFeature GetClosestWalletFeature(int version) } return static_cast<WalletFeature>(0); } +} // namespace wallet diff --git a/src/wallet/walletutil.h b/src/wallet/walletutil.h index 06569d2f5f..788d41ceb7 100644 --- a/src/wallet/walletutil.h +++ b/src/wallet/walletutil.h @@ -10,6 +10,7 @@ #include <vector> +namespace wallet { /** (client) version numbers for particular wallet features */ enum WalletFeature { @@ -103,5 +104,6 @@ public: WalletDescriptor() {} WalletDescriptor(std::shared_ptr<Descriptor> descriptor, uint64_t creation_time, int32_t range_start, int32_t range_end, int32_t next_index) : descriptor(descriptor), creation_time(creation_time), range_start(range_start), range_end(range_end), next_index(next_index) {} }; +} // namespace wallet #endif // BITCOIN_WALLET_WALLETUTIL_H |