Files
bitcoin/src/wallet/wallettool.cpp
Samuel Dobson 0475a2378b Merge bitcoin/bitcoin#23349: util: Use FEATURE_LATEST for wallets created with bitcoin-wallet
5b6b5ef5d1 util: Use FEATURE_LATEST for wallets created with bitcoin-wallet (Hennadii Stepanov)

Pull request description:

  Since the 49d2374acf commit was athored by **jonasschnelli** in 2016, the wallet version was bumped twice: in 2017 (bitcoin/bitcoin#11250) and in 2018 (bitcoin/bitcoin#12560).

  This PR bumps the version of wallets created with `bitcoin-wallet` offline tool.

  On master (04437ee721) -- `"walletversion": 139900`:
  ```
  $ src/bitcoin-wallet -signet -wallet=211025-test-master create
  Topping up keypool...
  Wallet info
  ===========
  Name: 211025-test-master
  Format: sqlite
  Descriptors: yes
  Encrypted: no
  HD (hd seed available): yes
  Keypool Size: 6000
  Transactions: 0
  Address Book: 0
  $ src/bitcoin-cli -signet -rpcwallet=211025-test-master getwalletinfo
  {
    "walletname": "211025-test-master",
    "walletversion": 139900,
    "format": "sqlite",
    "balance": 0.00000000,
    "unconfirmed_balance": 0.00000000,
    "immature_balance": 0.00000000,
    "txcount": 0,
    "keypoolsize": 3000,
    "keypoolsize_hd_internal": 3000,
    "paytxfee": 0.00000000,
    "private_keys_enabled": true,
    "avoid_reuse": false,
    "scanning": false,
    "descriptors": true
  }
  ```

  With this PR -- `"walletversion": 169900`:
  ```
  $ src/bitcoin-wallet -signet -wallet=211025-test-pr create
  Topping up keypool...
  Wallet info
  ===========
  Name: 211025-test-pr
  Format: sqlite
  Descriptors: yes
  Encrypted: no
  HD (hd seed available): yes
  Keypool Size: 6000
  Transactions: 0
  Address Book: 0
  $ src/bitcoin-cli -signet -rpcwallet=211025-test-pr getwalletinfo
  {
    "walletname": "211025-test-pr",
    "walletversion": 169900,
    "format": "sqlite",
    "balance": 0.00000000,
    "unconfirmed_balance": 0.00000000,
    "immature_balance": 0.00000000,
    "txcount": 0,
    "keypoolsize": 3000,
    "keypoolsize_hd_internal": 3000,
    "paytxfee": 0.00000000,
    "private_keys_enabled": true,
    "avoid_reuse": false,
    "scanning": false,
    "descriptors": true
  }
  ```

ACKs for top commit:
  lsilva01:
    Code Review ACK 5b6b5ef
  stratospher:
    ACK 5b6b5ef.
  rajarshimaitra:
    ACK 5b6b5ef5d1
  meshcollider:
    Code review ACK 5b6b5ef5d1

Tree-SHA512: 0221e76fa8f29037920d0a483c742bf270ecaead45f30230943b78775aaea63ac052e43fe712d15c2326e515dea2d2ac82de0924882598421c1874f2e6f442a6
2021-11-16 15:32:18 +13:00

216 lines
9.0 KiB
C++

// Copyright (c) 2016-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <fs.h>
#include <util/system.h>
#include <util/translation.h>
#include <wallet/dump.h>
#include <wallet/salvage.h>
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
namespace WalletTool {
// The standard wallet deleter function blocks on the validation interface
// queue, which doesn't exist for the bitcoin-wallet. Define our own
// deleter here.
static void WalletToolReleaseWallet(CWallet* wallet)
{
wallet->WalletLogPrintf("Releasing wallet\n");
wallet->Close();
delete wallet;
}
static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flags)
{
LOCK(wallet_instance->cs_wallet);
wallet_instance->SetMinVersion(FEATURE_LATEST);
wallet_instance->AddWalletFlags(wallet_creation_flags);
if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
spk_man->SetupGeneration(false);
} else {
wallet_instance->SetupDescriptorScriptPubKeyMans();
}
tfm::format(std::cout, "Topping up keypool...\n");
wallet_instance->TopUpKeyPool();
}
static const std::shared_ptr<CWallet> MakeWallet(const std::string& name, const fs::path& path, const ArgsManager& args, DatabaseOptions options)
{
DatabaseStatus status;
bilingual_str error;
std::unique_ptr<WalletDatabase> database = MakeDatabase(path, options, status, error);
if (!database) {
tfm::format(std::cerr, "%s\n", error.original);
return nullptr;
}
// dummy chain interface
std::shared_ptr<CWallet> wallet_instance{new CWallet(nullptr /* chain */, name, args, std::move(database)), WalletToolReleaseWallet};
DBErrors load_wallet_ret;
try {
load_wallet_ret = wallet_instance->LoadWallet();
} catch (const std::runtime_error&) {
tfm::format(std::cerr, "Error loading %s. Is wallet being used by another process?\n", name);
return nullptr;
}
if (load_wallet_ret != DBErrors::LOAD_OK) {
wallet_instance = nullptr;
if (load_wallet_ret == DBErrors::CORRUPT) {
tfm::format(std::cerr, "Error loading %s: Wallet corrupted", name);
return nullptr;
} else if (load_wallet_ret == DBErrors::NONCRITICAL_ERROR) {
tfm::format(std::cerr, "Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect.",
name);
} else if (load_wallet_ret == DBErrors::TOO_NEW) {
tfm::format(std::cerr, "Error loading %s: Wallet requires newer version of %s",
name, PACKAGE_NAME);
return nullptr;
} else if (load_wallet_ret == DBErrors::NEED_REWRITE) {
tfm::format(std::cerr, "Wallet needed to be rewritten: restart %s to complete", PACKAGE_NAME);
return nullptr;
} else if (load_wallet_ret == DBErrors::NEED_RESCAN) {
tfm::format(std::cerr, "Error reading %s! Some transaction data might be missing or"
" incorrect. Wallet requires a rescan.",
name);
} else {
tfm::format(std::cerr, "Error loading %s", name);
return nullptr;
}
}
if (options.require_create) WalletCreate(wallet_instance.get(), options.create_flags);
return wallet_instance;
}
static void WalletShowInfo(CWallet* wallet_instance)
{
LOCK(wallet_instance->cs_wallet);
tfm::format(std::cout, "Wallet info\n===========\n");
tfm::format(std::cout, "Name: %s\n", wallet_instance->GetName());
tfm::format(std::cout, "Format: %s\n", wallet_instance->GetDatabase().Format());
tfm::format(std::cout, "Descriptors: %s\n", wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS) ? "yes" : "no");
tfm::format(std::cout, "Encrypted: %s\n", wallet_instance->IsCrypted() ? "yes" : "no");
tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no");
tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
{
if (args.IsArgSet("-format") && command != "createfromdump") {
tfm::format(std::cerr, "The -format option can only be used with the \"createfromdump\" command.\n");
return false;
}
if (args.IsArgSet("-dumpfile") && command != "dump" && command != "createfromdump") {
tfm::format(std::cerr, "The -dumpfile option can only be used with the \"dump\" and \"createfromdump\" commands.\n");
return false;
}
if (args.IsArgSet("-descriptors") && command != "create") {
tfm::format(std::cerr, "The -descriptors option can only be used with the 'create' command.\n");
return false;
}
if (args.IsArgSet("-legacy") && command != "create") {
tfm::format(std::cerr, "The -legacy option can only be used with the 'create' command.\n");
return false;
}
if (command == "create" && !args.IsArgSet("-wallet")) {
tfm::format(std::cerr, "Wallet name must be provided when creating a new wallet.\n");
return false;
}
const std::string name = args.GetArg("-wallet", "");
const fs::path path = fsbridge::AbsPathJoin(GetWalletDir(), fs::PathFromString(name));
if (command == "create") {
DatabaseOptions options;
options.require_create = true;
// If -legacy is set, use it. Otherwise default to false.
bool make_legacy = args.GetBoolArg("-legacy", false);
// If neither -legacy nor -descriptors is set, default to true. If -descriptors is set, use its value.
bool make_descriptors = (!args.IsArgSet("-descriptors") && !args.IsArgSet("-legacy")) || (args.IsArgSet("-descriptors") && args.GetBoolArg("-descriptors", true));
if (make_legacy && make_descriptors) {
tfm::format(std::cerr, "Only one of -legacy or -descriptors can be set to true, not both\n");
return false;
}
if (!make_legacy && !make_descriptors) {
tfm::format(std::cerr, "One of -legacy or -descriptors must be set to true (or omitted)\n");
return false;
}
if (make_descriptors) {
options.create_flags |= WALLET_FLAG_DESCRIPTORS;
options.require_format = DatabaseFormat::SQLITE;
}
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
}
} else if (command == "info") {
DatabaseOptions options;
options.require_existing = true;
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
} else if (command == "salvage") {
#ifdef USE_BDB
bilingual_str error;
std::vector<bilingual_str> warnings;
bool ret = RecoverDatabaseFile(path, error, warnings);
if (!ret) {
for (const auto& warning : warnings) {
tfm::format(std::cerr, "%s\n", warning.original);
}
if (!error.empty()) {
tfm::format(std::cerr, "%s\n", error.original);
}
}
return ret;
#else
tfm::format(std::cerr, "Salvage command is not available as BDB support is not compiled");
return false;
#endif
} else if (command == "dump") {
DatabaseOptions options;
options.require_existing = true;
const std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, args, options);
if (!wallet_instance) return false;
bilingual_str error;
bool ret = DumpWallet(*wallet_instance, error);
if (!ret && !error.empty()) {
tfm::format(std::cerr, "%s\n", error.original);
return ret;
}
tfm::format(std::cout, "The dumpfile may contain private keys. To ensure the safety of your Bitcoin, do not share the dumpfile.\n");
return ret;
} else if (command == "createfromdump") {
bilingual_str error;
std::vector<bilingual_str> warnings;
bool ret = CreateFromDump(name, path, error, warnings);
for (const auto& warning : warnings) {
tfm::format(std::cout, "%s\n", warning.original);
}
if (!ret && !error.empty()) {
tfm::format(std::cerr, "%s\n", error.original);
}
return ret;
} else {
tfm::format(std::cerr, "Invalid command: %s\n", command);
return false;
}
return true;
}
} // namespace WalletTool