Add available field to listwallets RPC#11485
Add available field to listwallets RPC#11485meshcollider wants to merge 3 commits intobitcoin:masterfrom meshcollider:201710_listwallets_available
available field to listwallets RPC#11485Conversation
|
As said in #11466 (comment), a different interface is: {
"wallets": [
{ "name": "foo", "loaded": true },
{ "name": "bar", "loaded": false }
]
}Which has the advantage of allowing to add new attributes later. Another option is to have a different RPC. So:
This has the advantage that each RPC has it's own purpose and implementation, which happen to be very different. For those that manage (keep record of) the wallets externally |
|
concept ACK splitting this from #11466. And concept ACK using the BDB magic bytes rather than filename |
available field to listwallets RPCavailable field to listwallets RPC
|
Removed WIP, now uses BDB magic bytes, although I haven't confirmed that this works on big endian or also potentially on old versions of BDB wallets if the magic bytes changed |
jnewbery
left a comment
There was a problem hiding this comment.
Looks good. Can you extend the checking to exclude symlinks from the available wallets list, and add a test to cover that?
I think it might also be a good idea to exclude currently loaded wallets from the available list (ie the loaded and available lists should be mutually exclusive)
src/wallet/rpcwallet.cpp
Outdated
There was a problem hiding this comment.
Please explicitly use fs::is_regular_file() (this only compiles because of Argument Dependant Lookups http://en.cppreference.com/w/cpp/language/adl which makes it difficult to understand where this function is coming from).
boost::filesystem::is_regular_file() will return true if called on a symlink to a regular file. We don't want those to show up in the available list since they can't be loaded as a wallet, so you should extend this if test with fs::is_symlink().
|
I also tested manually that files show up as wallets when bytes 12-15 match the magic bdb bytes: |
|
Addressed @jnewbery nits and made available list mutually exclusive to loaded list. Thanks for the review :-) |
src/wallet/rpcwallet.cpp
Outdated
There was a problem hiding this comment.
Suggestion to remove loop below:
std::set<std::string> loaded_set;
...
loaded.push_back(pwallet->GetName());
loaded_set.insert(pwallet->GetName())
}
...
if (loaded_set.count(it->path().filename().string()) == 0) {
available.push_back(it->path().filename().string());
}|
Good idea @promag, thanks :-) |
src/wallet/rpcwallet.cpp
Outdated
There was a problem hiding this comment.
Some of these conditions can be moved before seekg (just continue. Only the compare is needed.
Could use 64 bit int instead?
There was a problem hiding this comment.
Fixed the conditional, but IMO the char array is clearer and perhaps safer than using an int :)
|
Rebased on master to fix travis failure due to multiwallet.py being modified |
src/wallet/rpcwallet.cpp
Outdated
There was a problem hiding this comment.
Remove, see https://stackoverflow.com/a/748059.
src/wallet/rpcwallet.cpp
Outdated
There was a problem hiding this comment.
Alternative, avoid nesting:
if (memcmp(...) != 0) continue;
if (loaded_set.count(filename) > 0) continue;
available.push_back(filename);Note that close is automatically called, see comment below.
There was a problem hiding this comment.
Nit, add:
const std::string filename = it->path().filename().string();
src/wallet/rpcwallet.cpp
Outdated
src/wallet/rpcwallet.cpp
Outdated
test/functional/multiwallet.py
Outdated
There was a problem hiding this comment.
Assert available is empty array?
|
Rebased and updated to work with new wallets directory from #11466 |
|
Needs rebase, but I assume this won't work after #11687 anyway? |
It won't work if someone uses external wallets, but it should still work if they have all wallets in the wallet directory? (which is the default) |
|
I've rebased. If #11687 defaults to creating directories for each wallet rather than individual BDB files, then this may need to be modified to list directories as well as BDB files? But yes should still work |
|
|
||
| self.start_node(0, self.extra_args[0]) | ||
|
|
||
| # ensure listwallets only names BDB files, not symlinks, other files or directories |
There was a problem hiding this comment.
I like this new test, but I think it can be improved in a couple of ways:
- test that a non-empty file without the magic bytes is excluded
- there's an implicit assumption that the symlink and folder created earlier in the test are still around. You could explicitly assert that. If the earlier parts of this test get change, eg the file names are changed, then this check will continue to succeed but won't be testing anything.
What do you think about:
--- a/test/functional/multiwallet.py
+++ b/test/functional/multiwallet.py
@@ -65,8 +65,20 @@ class MultiWalletTest(BitcoinTestFramework):
self.start_node(0, self.extra_args[0])
# ensure listwallets only names BDB files, not symlinks, other files or directories
- open(os.path.join(wallet_dir, 'w01.dat'), 'a').close()
- assert_equal(set(self.nodes[0].listwallets()['available']), {"w22"})
+ with open(os.path.join(wallet_dir, 'w01.dat'), 'a') as wallet_file:
+ # Create a file containing a long string
+ print("a" * 2000, file=wallet_file)
+
+ available_wallets = self.nodes[0].listwallets()['available']
+
+ assert os.path.islink(os.path.join(wallet_dir, 'w12'))
+ assert 'w12' not in available_wallets
+
+ assert os.path.isdir(os.path.join(wallet_dir, 'w11'))
+ assert 'w11' not in available_wallets
+
+ assert os.path.isfile(os.path.join(wallet_dir, 'w01.dat'))
+ assert 'w01.dat' not in available_wallets
TheBlueMatt
left a comment
There was a problem hiding this comment.
Code looks good, RPC docs could be more descriptive (and are out-of-date after the change).
| " \"walletname\" (string) the wallet name\n" | ||
| " ...\n" | ||
| "]\n" | ||
| "{\n" |
There was a problem hiding this comment.
Need to update the docs above. Maybe also note that false-positives may include files from exceedingly old (pre-0.8, I believe) nodes as well as duplicates of wallets which cannot be opened simultaneously.
|
Update the documentation as per @TheBlueMatt suggestion |
| - The wallet RPC `getreceivedbyaddress` will return an error if called with an address not in the wallet. | ||
|
|
||
|
|
||
| - `listwallets` now returns two arrays of wallet names, one listing the `loaded` wallets |
There was a problem hiding this comment.
I don't think it's a big deal to break backwards compatibility here, but I also don't see any benefit in doing so. I'd think that a better approach to overloading the listwallets function would be to add a new listwalletdir function that would return a list of wallet files inside -walletdir. Advantages to this approach:
- Backwards compatible
- Better performance for listwallets. It could continue to just return the loaded wallets from memory instead of having now having to do disk io.
- More descriptive function name.
listwalletdirnaming is consistent with recently addedwalletdirparameter, and tells you what the function actually does.
src/wallet/rpcwallet.cpp
Outdated
| "]\n" | ||
| "{\n" | ||
| " \"loaded\": [ \"walletname\" ... ] (json array of strings) Names of loaded wallets\n" | ||
| " \"available\": [ \"walletname\" ... ] (json array of strings) Names of BDB files in wallet directory (may not always actually be wallets)\n" |
There was a problem hiding this comment.
Would suggest returning an array of objects rather than an array of strings to future proof this. This would allow including other information like sizes, last modification times, whether databases are flushed, or locked by another process. (Latter two would be meaningful after #11687 when wallet files can be opened in their own berkelydb environments instead of all using the same shared environment).
There was a problem hiding this comment.
Partially agree with @ryanofsky. Still think one array is enough, see #11485 (comment). And listwallets could have an optional parameter to filter available, loaded or both.
| for (fs::directory_iterator it(walletdir); it != fs::directory_iterator(); ++it) { | ||
| if (!fs::is_regular_file(*it) || fs::is_symlink(*it)) continue; | ||
|
|
||
| std::ifstream file(it->path().string(), std::ios::binary); |
There was a problem hiding this comment.
Note: to be compatible with #11687, you would drop the continue above and change this line to something like:
std::ifstream file(fs::is_regular_file(*it) ? it->path() : (it->path() / "wallet.dat")).string(), std::ios::binary);|
Might be ready for merge. Unclear if "Code looks good" from @TheBlueMatt above is equivalent to a code ack. |
|
@ryanofsky sorry I haven't had time to work on your suggested changes above, I'm happy to let this wait until I've had time to address them |
|
Closing for now w/ up for grabs, let me know when you start working on this and it needs to be reopened. |
|
Shame this has gone stale. I think it's useful new functionality. I'd be very happy to re-review if someone picked this up again. |
|
I'm tempted to pick this as this is also useful in the UI — must be exposed in interfaces::Nodes. As suggested by @Sjors in #13100 (review)
Thoughts? |
ListWalletDir returns all available wallets in the current wallet directory. Based on MeshCollider work in pull bitcoin#11485.
d56a068 docs: Add release notes for listwalletdir RPC (João Barbosa) 0cb3cad qa: Add tests for listwalletdir RPC (João Barbosa) cc33773 rpc: Add listwalletdir RPC (João Barbosa) d1b03b8 interfaces: Add getWalletDir and listWalletDir to Node (João Barbosa) fc4db35 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
ListWalletDir returns all available wallets in the current wallet directory. Based on MeshCollider work in pull bitcoin#11485.
ListWalletDir returns all available wallets in the current wallet directory. Based on MeshCollider work in pull bitcoin#11485.
d56a068 docs: Add release notes for listwalletdir RPC (João Barbosa) 0cb3cad qa: Add tests for listwalletdir RPC (João Barbosa) cc33773 rpc: Add listwalletdir RPC (João Barbosa) d1b03b8 interfaces: Add getWalletDir and listWalletDir to Node (João Barbosa) fc4db35 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 bitcoin#11485. Tree-SHA512: 5843e3dbd1e0449f55bb8ea7c241a536078ff6ffcaad88ce5fcf8963971d48c78600fbc4f44919523b8a92329d5d8a5f567a3e0ccb0270fdd27366e19603a716
d56a068 docs: Add release notes for listwalletdir RPC (João Barbosa) 0cb3cad qa: Add tests for listwalletdir RPC (João Barbosa) cc33773 rpc: Add listwalletdir RPC (João Barbosa) d1b03b8 interfaces: Add getWalletDir and listWalletDir to Node (João Barbosa) fc4db35 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 bitcoin#11485. Tree-SHA512: 5843e3dbd1e0449f55bb8ea7c241a536078ff6ffcaad88ce5fcf8963971d48c78600fbc4f44919523b8a92329d5d8a5f567a3e0ccb0270fdd27366e19603a716
Modifies
listwalletsto return a list of 'available' wallets in the wallet directory, by looking for the BDB magic bytes (0x00053162source: https://github.com/file/file/blob/master/magic/Magdir/database) .This is a breaking change to listwallets, but multiwallet RPC calls are experimental in 0.15 so shouldn't be a big concern to change it. c.f. @jnewbery's comment here. Split from #11466.
Would be great if someone could confirm if this is endianness independent too :)