mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-21 14:34:49 +01:00
Merge #14291: wallet: Add ListWalletDir utility function
d56a068935
docs: Add release notes for listwalletdir RPC (João Barbosa)0cb3cad166
qa: Add tests for listwalletdir RPC (João Barbosa)cc3377360c
rpc: Add listwalletdir RPC (João Barbosa)d1b03b8e5f
interfaces: Add getWalletDir and listWalletDir to Node (João Barbosa)fc4db35bfd
wallet: Add ListWalletDir utility (João Barbosa) Pull request description: `ListWalletDir` returns all available wallets in the current wallet directory. Based on MeshCollider work in pull #11485. Tree-SHA512: 5843e3dbd1e0449f55bb8ea7c241a536078ff6ffcaad88ce5fcf8963971d48c78600fbc4f44919523b8a92329d5d8a5f567a3e0ccb0270fdd27366e19603a716
This commit is contained in:
commit
8eb2cd1dda
8 changed files with 129 additions and 3 deletions
|
@ -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.
|
||||
|
|
|
@ -34,6 +34,16 @@ void DummyWalletInit::AddWalletOptions() const
|
|||
|
||||
const WalletInitInterface& g_wallet_init_interface = DummyWalletInit();
|
||||
|
||||
fs::path GetWalletDir()
|
||||
{
|
||||
throw std::logic_error("Wallet function called in non-wallet build.");
|
||||
}
|
||||
|
||||
std::vector<fs::path> ListWalletDir()
|
||||
{
|
||||
throw std::logic_error("Wallet function called in non-wallet build.");
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<CWallet>> GetWallets()
|
||||
{
|
||||
throw std::logic_error("Wallet function called in non-wallet build.");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -173,6 +173,12 @@ public:
|
|||
//! 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;
|
||||
|
||||
|
|
|
@ -2443,6 +2443,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)
|
||||
|
@ -4096,6 +4128,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"} },
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -38,6 +38,8 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||
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)
|
||||
|
@ -71,6 +73,8 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||
wallet_names.remove('w7_symlink')
|
||||
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']))
|
||||
|
||||
assert_equal(set(node.listwallets()), set(wallet_names))
|
||||
|
||||
# check that all requested wallets were created
|
||||
|
@ -143,6 +147,8 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||
|
||||
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")
|
||||
|
||||
|
@ -281,6 +287,8 @@ class MultiWalletTest(BitcoinTestFramework):
|
|||
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'])
|
||||
|
|
Loading…
Add table
Reference in a new issue