Merge 23362 via importfromcoldcard

This commit is contained in:
Luke Dashjr 2024-06-21 19:28:12 +00:00
commit f81f980843
3 changed files with 83 additions and 3 deletions

View File

@ -50,6 +50,7 @@ static void SetupWalletToolArgs(ArgsManager& argsman)
argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.");
argsman.AddCommand("dump", "Print out all of the wallet key-value records");
argsman.AddCommand("createfromdump", "Create new wallet file from dumped records");
argsman.AddCommand("importfromcoldcard", "Create new wallet file and import descriptors from Coldcard wallet");
}
static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[])

View File

@ -1447,7 +1447,7 @@ RPCHelpMan importmulti()
};
}
static UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet)
{
UniValue warnings(UniValue::VARR);
UniValue result(UniValue::VOBJ);

View File

@ -9,6 +9,8 @@
#include <wallet/wallettool.h>
#include <common/args.h>
#include <tinyformat.h>
#include <univalue.h>
#include <util/fs.h>
#include <util/translation.h>
#include <wallet/dump.h>
@ -16,7 +18,15 @@
#include <wallet/wallet.h>
#include <wallet/walletutil.h>
#include <cassert>
#include <fstream>
#include <string>
namespace wallet {
UniValue ProcessDescriptorImport(CWallet& wallet, const UniValue& data, const int64_t timestamp)
EXCLUSIVE_LOCKS_REQUIRED(wallet.cs_wallet);
namespace WalletTool {
// The standard wallet deleter function blocks on the validation interface
@ -36,6 +46,10 @@ static void WalletCreate(CWallet* wallet_instance, uint64_t wallet_creation_flag
wallet_instance->SetMinVersion(FEATURE_LATEST);
wallet_instance->InitWalletFlags(wallet_creation_flags);
if (wallet_instance->IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {
return;
}
if (!wallet_instance->IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)) {
auto spk_man = wallet_instance->GetOrCreateLegacyScriptPubKeyMan();
spk_man->SetupGeneration(false);
@ -112,14 +126,40 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
static bool ReadAndParseColdcardFile(const fs::path& path, UniValue& decriptors)
{
std::ifstream file;
file.open(path);
if (!file.is_open()) {
tfm::format(std::cerr, "%s. Please check permissions.\n", fs::PathToString(path));
return false;
}
std::string line;
while (std::getline(file, line)) {
if (line.substr(0, 22) == "importdescriptors \'[{\"") break;
}
file.close();
decriptors.clear();
if (!decriptors.read(line.substr(19, line.size() - 20))) {
tfm::format(std::cerr, "Unable to parse %s\n", fs::PathToString(path));
return false;
}
assert(decriptors.isArray());
return true;
}
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");
if (args.IsArgSet("-dumpfile") && command != "dump" && command != "createfromdump" && command != "importfromcoldcard") {
tfm::format(std::cerr, "The -dumpfile option can only be used with the \"dump\", \"createfromdump\" and \"importfromcoldcard\" commands.\n");
return false;
}
if (args.IsArgSet("-descriptors") && command != "create") {
@ -228,6 +268,45 @@ bool ExecuteWalletToolFunc(const ArgsManager& args, const std::string& command)
tfm::format(std::cerr, "%s\n", error.original);
}
return ret;
} else if (command == "importfromcoldcard") {
tfm::format(std::cerr, "WARNING: The \"importfromcoldcard\" command is experimental and will likely be removed or changed incompatibly in a future version.\n");
std::string filename = gArgs.GetArg("-dumpfile", "");
if (filename.empty()) {
tfm::format(std::cerr, "To use importfromcoldcard, -dumpfile=<filename> must be provided.\n");
return false;
}
const fs::path import_file_path{fs::absolute(fs::PathFromString(filename))};
if (!fs::exists(import_file_path)) {
tfm::format(std::cerr, "File %s does not exist.\n", fs::PathToString(import_file_path));
return false;
}
UniValue descriptors;
if (!ReadAndParseColdcardFile(import_file_path, descriptors)) {
return false;
}
DatabaseOptions options;
options.require_create = true;
options.create_flags |= WALLET_FLAG_DESCRIPTORS;
options.create_flags |= WALLET_FLAG_DISABLE_PRIVATE_KEYS;
options.create_flags |= WALLET_FLAG_BLANK_WALLET;
options.require_format = DatabaseFormat::SQLITE;
std::shared_ptr<CWallet> wallet_instance = MakeWallet(name, path, options);
if (!wallet_instance) {
return false;
}
LOCK(wallet_instance->cs_wallet);
for (const UniValue& descriptor : descriptors.getValues()) {
const UniValue result = ProcessDescriptorImport(*wallet_instance, descriptor, 0);
tfm::format(std::cerr, "%s\n", result.write(2));
}
WalletShowInfo(wallet_instance.get());
wallet_instance->Close();
} else {
tfm::format(std::cerr, "Invalid command: %s\n", command);
return false;