limenka-wallet.cpp raw
1 // Copyright (c) 2016-present The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <limenka-build-config.h> // IWYU pragma: keep
6
7 #include <chainparams.h>
8 #include <chainparamsbase.h>
9 #include <clientversion.h>
10 #include <common/args.h>
11 #include <common/system.h>
12 #include <compat/compat.h>
13 #include <interfaces/init.h>
14 #include <key.h>
15 #include <logging.h>
16 #include <pubkey.h>
17 #include <tinyformat.h>
18 #include <util/exception.h>
19 #include <util/translation.h>
20 #include <wallet/wallettool.h>
21
22 #include <exception>
23 #include <functional>
24 #include <string>
25 #include <tuple>
26
27 using util::Join;
28
29 const TranslateFn G_TRANSLATION_FUN{nullptr};
30
31 static void SetupWalletToolArgs(ArgsManager& argsman)
32 {
33 SetupHelpOptions(argsman);
34 SetupChainParamsBaseOptions(argsman);
35
36 argsman.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
37 argsman.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
38 argsman.AddArg("-wallet=<wallet-name>", "Specify wallet name", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS);
39 argsman.AddArg("-dumpfile=<file name>", "When used with 'dump', writes out the records to this file. When used with 'createfromdump', loads the records into a new wallet.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS);
40 argsman.AddArg("-debug=<category>", "Output debugging information (default: 0).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
41 argsman.AddArg("-descriptors", "Create descriptors wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
42 argsman.AddArg("-legacy", "Create legacy wallet. Only for 'create'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
43 argsman.AddArg("-format=<format>", "The format of the wallet file to create. Either \"bdb\" or \"sqlite\". Only used with 'createfromdump'", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
44 argsman.AddArg("-printtoconsole", "Send trace/debug info to console (default: 1 when no -debug is true, 0 otherwise).", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
45 argsman.AddArg("-withinternalbdb", "Use the internal Berkeley DB parser when dumping a Berkeley DB wallet file (default: false)", ArgsManager::ALLOW_ANY, OptionsCategory::DEBUG_TEST);
46
47 argsman.AddCommand("info", "Get wallet info");
48 argsman.AddCommand("create", "Create new wallet file");
49 argsman.AddCommand("salvage", "Attempt to recover private keys from a corrupt wallet. Warning: 'salvage' is experimental.");
50 argsman.AddCommand("dump", "Print out all of the wallet key-value records");
51 argsman.AddCommand("createfromdump", "Create new wallet file from dumped records");
52 argsman.AddCommand("importfromcoldcard", "Create new wallet file and import descriptors from Coldcard wallet");
53 }
54
55 static std::optional<int> WalletAppInit(ArgsManager& args, int argc, char* argv[])
56 {
57 SetupWalletToolArgs(args);
58 std::string error_message;
59 if (!args.ParseParameters(argc, argv, error_message)) {
60 tfm::format(std::cerr, "Error parsing command line arguments: %s\n", error_message);
61 return EXIT_FAILURE;
62 }
63 const bool missing_args{argc < 2};
64 if (missing_args || HelpRequested(args) || args.GetBoolArg("-version", false)) {
65 std::string strUsage = strprintf("%s limenka-wallet utility version", CLIENT_NAME) + " " + FormatFullVersion() + "\n";
66
67 if (args.GetBoolArg("-version", false)) {
68 strUsage += FormatParagraph(LicenseInfo());
69 } else {
70 strUsage += "\n"
71 "limenka-wallet is an offline tool for creating and interacting with " CLIENT_NAME " wallet files.\n\n"
72 "By default limenka-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n\n"
73 "To change the target wallet, use the -datadir, -wallet and (test)chain selection arguments.\n"
74 "\n"
75 "Usage: limenka-wallet [options] <command>\n"
76 "\n";
77 strUsage += "\n" + args.GetHelpMessage();
78 }
79 tfm::format(std::cout, "%s", strUsage);
80 if (missing_args) {
81 tfm::format(std::cerr, "Error: too few parameters\n");
82 return EXIT_FAILURE;
83 }
84 return EXIT_SUCCESS;
85 }
86
87 // check for printtoconsole, allow -debug
88 LogInstance().m_print_to_console = args.GetBoolArg("-printtoconsole", args.GetBoolArg("-debug", false));
89
90 if (!CheckDataDirOption(args)) {
91 tfm::format(std::cerr, "Error: Specified data directory \"%s\" does not exist.\n", args.GetArg("-datadir", ""));
92 return EXIT_FAILURE;
93 }
94 // Check for chain settings (Params() calls are only valid after this clause)
95 SelectParams(args.GetChainType());
96
97 return std::nullopt;
98 }
99
100 MAIN_FUNCTION
101 {
102 ArgsManager& args = gArgs;
103 #ifdef WIN32
104 common::WinCmdLineArgs winArgs;
105 std::tie(argc, argv) = winArgs.get();
106 #endif
107
108 int exit_status;
109 std::unique_ptr<interfaces::Init> init = interfaces::MakeWalletInit(argc, argv, exit_status);
110 if (!init) {
111 return exit_status;
112 }
113
114 SetupEnvironment();
115 RandomInit();
116 try {
117 if (const auto maybe_exit{WalletAppInit(args, argc, argv)}) return *maybe_exit;
118 } catch (const std::exception& e) {
119 PrintExceptionContinue(&e, "WalletAppInit()");
120 return EXIT_FAILURE;
121 } catch (...) {
122 PrintExceptionContinue(nullptr, "WalletAppInit()");
123 return EXIT_FAILURE;
124 }
125
126 const auto command = args.GetCommand();
127 if (!command) {
128 tfm::format(std::cerr, "No method provided. Run `limenka-wallet -help` for valid methods.\n");
129 return EXIT_FAILURE;
130 }
131 if (command->args.size() != 0) {
132 tfm::format(std::cerr, "Error: Additional arguments provided (%s). Methods do not take arguments. Please refer to `-help`.\n", Join(command->args, ", "));
133 return EXIT_FAILURE;
134 }
135
136 ECC_Context ecc_context{};
137 if (!wallet::WalletTool::ExecuteWalletToolFunc(args, command->command)) {
138 return EXIT_FAILURE;
139 }
140 return EXIT_SUCCESS;
141 }
142