-
Notifications
You must be signed in to change notification settings - Fork 38.6k
wallet: Add ListWalletDir utility function #14291
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
fc4db35
d1b03b8
cc33773
0cb3cad
d56a068
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| New RPC methods | ||
| ------------ | ||
|
|
||
| - `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds. | ||
| - `getnodeaddresses` returns peer addresses known to this node. It may be used to connect to nodes over TCP without using the DNS seeds. | ||
| - `listwalletdir` returns a list of wallets in the wallet directory which is configured with `-walletdir` parameter. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,8 @@ | |
| #include <univalue.h> | ||
|
|
||
| class CWallet; | ||
| fs::path GetWalletDir(); | ||
| std::vector<fs::path> ListWalletDir(); | ||
| std::vector<std::shared_ptr<CWallet>> GetWallets(); | ||
|
|
||
| namespace interfaces { | ||
|
|
@@ -218,6 +220,18 @@ class NodeImpl : public Node | |
| LOCK(::cs_main); | ||
| return ::pcoinsTip->GetCoin(output, coin); | ||
| } | ||
| std::string getWalletDir() override | ||
| { | ||
| return GetWalletDir().string(); | ||
| } | ||
| std::vector<std::string> listWalletDir() override | ||
| { | ||
| std::vector<std::string> paths; | ||
| for (auto& path : ListWalletDir()) { | ||
| paths.push_back(path.string()); | ||
| } | ||
| return paths; | ||
|
||
| } | ||
|
||
| std::vector<std::unique_ptr<Wallet>> getWallets() override | ||
| { | ||
| std::vector<std::unique_ptr<Wallet>> wallets; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -173,6 +173,12 @@ class Node | |
| //! Get unspent outputs associated with a transaction. | ||
| virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0; | ||
|
|
||
| //! Return default wallet directory. | ||
| virtual std::string getWalletDir() = 0; | ||
|
|
||
| //! Return available wallets in wallet directory. | ||
| virtual std::vector<std::string> listWalletDir() = 0; | ||
|
||
|
|
||
| //! Return interfaces for accessing wallets (if any). | ||
| virtual std::vector<std::unique_ptr<Wallet>> getWallets() = 0; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2450,6 +2450,38 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) | |
| return obj; | ||
| } | ||
|
|
||
| static UniValue listwalletdir(const JSONRPCRequest& request) | ||
|
||
| { | ||
| if (request.fHelp || request.params.size() != 0) { | ||
| throw std::runtime_error( | ||
| "listwalletdir\n" | ||
| "Returns a list of wallets in the wallet directory.\n" | ||
| "{\n" | ||
| " \"wallets\" : [ (json array of objects)\n" | ||
| " {\n" | ||
| " \"name\" : \"name\" (string) The wallet name\n" | ||
| " }\n" | ||
| " ,...\n" | ||
| " ]\n" | ||
| "}\n" | ||
| "\nExamples:\n" | ||
| + HelpExampleCli("listwalletdir", "") | ||
| + HelpExampleRpc("listwalletdir", "") | ||
| ); | ||
| } | ||
|
|
||
| UniValue wallets(UniValue::VARR); | ||
| for (const auto& path : ListWalletDir()) { | ||
| UniValue wallet(UniValue::VOBJ); | ||
| wallet.pushKV("name", path.string()); | ||
| wallets.push_back(wallet); | ||
| } | ||
|
|
||
| UniValue result(UniValue::VOBJ); | ||
| result.pushKV("wallets", wallets); | ||
| return result; | ||
| } | ||
|
|
||
| static UniValue listwallets(const JSONRPCRequest& request) | ||
| { | ||
| if (request.fHelp || request.params.size() != 0) | ||
|
|
@@ -4102,6 +4134,7 @@ static const CRPCCommand commands[] = | |
| { "wallet", "listsinceblock", &listsinceblock, {"blockhash","target_confirmations","include_watchonly","include_removed"} }, | ||
| { "wallet", "listtransactions", &listtransactions, {"dummy","count","skip","include_watchonly"} }, | ||
| { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, | ||
| { "wallet", "listwalletdir", &listwalletdir, {} }, | ||
| { "wallet", "listwallets", &listwallets, {} }, | ||
| { "wallet", "loadwallet", &loadwallet, {"filename"} }, | ||
| { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |
|
|
||
| #include <wallet/walletutil.h> | ||
|
|
||
| #include <util.h> | ||
|
|
||
| fs::path GetWalletDir() | ||
| { | ||
| fs::path path; | ||
|
|
@@ -25,3 +27,51 @@ fs::path GetWalletDir() | |
|
|
||
| return path; | ||
| } | ||
|
|
||
| static bool IsBerkeleyBtree(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; | ||
| if (fs::file_size(path, ec) < 4096) return false; | ||
|
|
||
| fs::ifstream file(path.string(), std::ios::binary); | ||
| if (!file.is_open()) return false; | ||
|
|
||
| file.seekg(12, std::ios::beg); // Magic bytes start at offset 12 | ||
| uint32_t data = 0; | ||
| file.read((char*) &data, sizeof(data)); // Read 4 bytes of file to compare against magic | ||
|
||
|
|
||
| // Berkeley DB Btree magic bytes, from: | ||
| // https://github.com/file/file/blob/5824af38469ec1ca9ac3ffd251e7afe9dc11e227/magic/Magdir/database#L74-L75 | ||
| // - big endian systems - 00 05 31 62 | ||
| // - little endian systems - 62 31 05 00 | ||
| return data == 0x00053162 || data == 0x62310500; | ||
|
||
| } | ||
|
|
||
| std::vector<fs::path> ListWalletDir() | ||
| { | ||
| const fs::path wallet_dir = GetWalletDir(); | ||
| std::vector<fs::path> paths; | ||
|
|
||
| for (auto it = fs::recursive_directory_iterator(wallet_dir); it != end(it); ++it) { | ||
| if (it->status().type() == fs::directory_file && IsBerkeleyBtree(it->path() / "wallet.dat")) { | ||
| // Found a directory which contains wallet.dat btree file, add it as a wallet. | ||
| paths.emplace_back(fs::relative(it->path(), wallet_dir)); | ||
| } else if (it.level() == 0 && it->symlink_status().type() == fs::regular_file && IsBerkeleyBtree(it->path())) { | ||
|
||
| if (it->path().filename() == "wallet.dat") { | ||
| // Found top-level wallet.dat btree file, add top level directory "" | ||
| // as a wallet. | ||
| paths.emplace_back(); | ||
| } else { | ||
| // Found top-level btree file not called wallet.dat. Current bitcoin | ||
| // software will never create these files but will allow them to be | ||
| // opened in a shared database environment for backwards compatibility. | ||
| // Add it to the list of available wallets. | ||
| paths.emplace_back(fs::relative(it->path(), wallet_dir)); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See: #14528 (travis: Compile once on xenial) |
||
| } | ||
| } | ||
| } | ||
|
|
||
| return paths; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,10 +5,14 @@ | |
| #ifndef BITCOIN_WALLET_WALLETUTIL_H | ||
| #define BITCOIN_WALLET_WALLETUTIL_H | ||
|
|
||
| #include <chainparamsbase.h> | ||
| #include <util.h> | ||
| #include <fs.h> | ||
|
|
||
| #include <vector> | ||
|
|
||
| //! Get the path of the wallet directory. | ||
| fs::path GetWalletDir(); | ||
|
|
||
| //! Get wallets in wallet directory. | ||
| std::vector<fs::path> ListWalletDir(); | ||
|
|
||
|
||
| #endif // BITCOIN_WALLET_WALLETUTIL_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,8 @@ def wallet_file(name): | |
| return wallet_dir(name, "wallet.dat") | ||
| return wallet_dir(name) | ||
|
|
||
| assert_equal(self.nodes[0].listwalletdir(), { 'wallets': [{ 'name': '' }] }) | ||
|
|
||
| # check wallet.dat is created | ||
| self.stop_nodes() | ||
| assert_equal(os.path.isfile(wallet_dir('wallet.dat')), True) | ||
|
|
@@ -68,6 +70,8 @@ def wallet_file(name): | |
| wallet_names = ['w1', 'w2', 'w3', 'w', 'sub/w5', os.path.join(self.options.tmpdir, 'extern/w6'), 'w7_symlink', 'w8', ''] | ||
| extra_args = ['-wallet={}'.format(n) for n in wallet_names] | ||
| self.start_node(0, extra_args) | ||
| assert_equal(set(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), set(['', 'w3', 'w2', 'sub/w5', 'w7', 'w7', 'w1', 'w8', 'w'])) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong, |
||
|
|
||
| assert_equal(set(node.listwallets()), set(wallet_names)) | ||
|
|
||
| # check that all requested wallets were created | ||
|
|
@@ -139,6 +143,8 @@ def wallet_file(name): | |
|
|
||
| self.restart_node(0, extra_args) | ||
|
|
||
| assert_equal(set(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), set(['', 'w3', 'w2', 'sub/w5', 'w7', 'w7', 'w8_copy', 'w1', 'w8', 'w'])) | ||
|
|
||
| wallets = [wallet(w) for w in wallet_names] | ||
| wallet_bad = wallet("bad") | ||
|
|
||
|
|
@@ -276,6 +282,8 @@ def wallet_file(name): | |
| assert_equal(self.nodes[0].listwallets(), ['w1']) | ||
| assert_equal(w1.getwalletinfo()['walletname'], 'w1') | ||
|
|
||
| assert_equal(set(map(lambda w: w['name'], self.nodes[0].listwalletdir()['wallets'])), set(['', 'w3', 'w2', 'sub/w5', 'w7', 'w9', 'w7', 'w8_copy', 'w1', 'w8', 'w'])) | ||
|
|
||
| # Test backing up and restoring wallets | ||
| self.log.info("Test wallet backup") | ||
| self.restart_node(0, ['-nowallet']) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit:
throwNotWalletBuild()?