This PR adds a menu entry Open Wallet to support loading existing wallets in the UI.
Fixes #7675.
If the wallet folder name contains Chinese, it will create a new folder that has same wallet file with a weird name on Windows, maybe the encoding issue (Not related)
Also, the seperator is incorrect on Windows

@ken2812221 does the problem exists if you use the RPC command?
It shows wallet not found on RPC. (Not related)
@ken2812221 but it does exists right? What if you launch the application with -wallet=d:\...?
-wallet=d:\錢包 does works, but the name displayed in Qt is incorrect

And it crash when I quit Qt
Checked that this isn't an issue on Linux.
Maybe windows or boost is using big5 encoding. which ends in a right square bracket ]
>>> '錢包'.encode('big5')
b'\xbf\xfa\xa5]'
>>> '錢包'.encode('big5').decode('utf8', 'replace')
'���]'
@ken2812221 thanks for the review and bug report. Let's move the bug discussion to #13103.
706 | @@ -701,6 +707,20 @@ void BitcoinGUI::openClicked() 707 | } 708 | } 709 | 710 | +void BitcoinGUI::openWalletClicked() 711 | +{ 712 | + QString fileName = QFileDialog::getExistingDirectory(this, tr("Open Wallet"),
Use QDir::toNativeSeparators to convert / to \ on Windows
Done.
^^
233 | + } 234 | + std::pair<std::unique_ptr<Wallet>, std::string> loadWallet(const std::string& wallet_file) override 235 | + { 236 | +#ifdef ENABLE_WALLET 237 | + try { 238 | + std::string dummy_warning;
This seems almost exactly duplicated from loadwallet() in rpcwallet.cpp. Does it make sense to factor it out to avoid repetition?
Yeah, I would like to move to WalletManager
249 | + AddWallet(wallet); 250 | + 251 | + wallet->postInitProcess(); 252 | + return { MakeWallet(*wallet), std::string() }; 253 | + } catch (...) { 254 | + return { std::unique_ptr<Wallet>(), "ops" };
I don't understand why you're passing back an error string if it's always the string ops
Ops, it's unfinished. Not sure if it should return the pair.
Removed, changed the return type.
712 | + QString fileName = QFileDialog::getExistingDirectory(this, tr("Open Wallet"), 713 | + QString(), 714 | + QFileDialog::ShowDirsOnly 715 | + | QFileDialog::DontResolveSymlinks); 716 | + if (fileName.isEmpty()) return; 717 | + const auto& result = m_node.loadWallet(fileName.toStdString());
The use of auto here makes it difficult to understand the result.second below.
No an issue now, return value is not necessary.
Concept ACK
715 | + QFileDialog::ShowDirsOnly 716 | + | QFileDialog::DontResolveSymlinks); 717 | + if (fileName.isEmpty()) return; 718 | + 719 | + std::vector<std::string> errors; 720 | + m_node.loadWallet(QDir::toNativeSeparators(fileName).toStdString(), errors);
I think that's a good future enhancement. Not necessary in the initial version IMO.
Rebased on master.
wallet_multiwallet.py fails
Rebased.
713 | @@ -707,6 +714,24 @@ void BitcoinGUI::openClicked() 714 | } 715 | } 716 | 717 | +void BitcoinGUI::openWalletClicked() 718 | +{ 719 | + QString fileName = QFileDialog::getExistingDirectory(this, tr("Open Wallet"), 720 | + QString(),
Suggestion: start in the data directory for the current network (mainnet/testnet/regtest) instead of the home directory, since that's where the wallet files and folders will usually be (and because it may be difficult to traverse down into .bitcoin).
713 | @@ -707,6 +714,24 @@ void BitcoinGUI::openClicked() 714 | } 715 | } 716 | 717 | +void BitcoinGUI::openWalletClicked() 718 | +{ 719 | + QString fileName = QFileDialog::getExistingDirectory(this, tr("Open Wallet"), 720 | + QString(), 721 | + QFileDialog::ShowDirsOnly
Why only show directories and not symlinks? Wallets can be .dat files.
Right.
323 | @@ -322,6 +324,15 @@ void BitcoinGUI::createActions() 324 | openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); 325 | openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); 326 | 327 | + m_new_wallet_action = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("New &Wallet..."), this);
IIUC the & sets the keyboard shortcut, so these should be unique (or absent).
Then I think it's best to omit shortcuts for now, WDYT?
I'm fine with either no shortcuts or unique ones.
On macOS when opening a wallet or creating a new wallet it suggests the directory from which opened QT by default. This also leads to strangeness in the dropdown list:
<img width="409" alt="paths" src="https://user-images.githubusercontent.com/10217/42512739-b47745ca-8455-11e8-8a56-7c4ad118085a.png">
The default should be the datadir, if that can be passed into the dialog.
For creating new wallet, I think a better approach is to ask for a wallet name, and not offer the user the option to put it in a different directory for the UI.
For opening an existing wallet I have a light preference for just scanning the default directory for wallets and showing a list of wallets to choose from. This also prevents confusion around wallets that are files vs. wallets that are directories. Offering multiple wallets and offering the ability to put wallets anywhere in the filesystem are two different features.
Code looks good to me, but I'm not qualified to tell if the memory / pointer management stuff is correct.
macOS threw a warning:
objc[17648]: Class FIFinderSyncExtensionHost is implemented in both /System/Library/PrivateFrameworks/FinderKit.framework/Versions/A/FinderKit (0x7fff86ae1c90) and /System/Library/PrivateFrameworks/FileProvider.framework/OverrideBundles/FinderSyncCollaborationFileProviderOverride.bundle/Contents/MacOS/FinderSyncCollaborationFileProviderOverride (0x113143cd8). One of the two will be used. Which one is undefined.
Or walletdir?
If present, probably yes. Afaik it's only created if there was no datadir at launch time, so at least in normal circumstances if /wallets exists, it should be non-confusing to use that as a default.
120 | + 121 | + wallet->postInitProcess(); 122 | + return wallet; 123 | +} 124 | + 125 | +std::shared_ptr<CWallet> LoadWallet(const std::string& wallet_file, std::string& error, std::string& warning)
nit: there's a lot of common code between LoadWallet and CreateWallet
Rebased.
A check box for watch-only in the GUI can probably wait. Not sure if it's useful, but not against it either.
357 | @@ -347,6 +358,9 @@ void BitcoinGUI::createActions() 358 | connect(usedSendingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedSendingAddresses())); 359 | connect(usedReceivingAddressesAction, SIGNAL(triggered()), walletFrame, SLOT(usedReceivingAddresses())); 360 | connect(openAction, SIGNAL(triggered()), this, SLOT(openClicked())); 361 | + connect(m_new_wallet_action, SIGNAL(triggered()), this, SLOT(newWallet())); 362 | + connect(m_open_wallet_action, SIGNAL(triggered()), this, SLOT(openWallet())); 363 | + connect(m_close_wallet_action, SIGNAL(triggered()), this, SLOT(closeWallet()));
Could use the new syntax here?
Fails wallet_disableprivatekeys.py. The wallet_creation_flags are not being passed through to CreateWalletFromFile(), and so this regresses being able to create a watch-only wallet.
With #9662 merged, do we want the same feature in the UI?
Later. Let's just try to get the basic functionality into the GUI first!
I'm a bit confused by git here: Github shows 737ecac as the most recent commit, but when I fetch your branch it's not there. Travis seems to ignore it as well.
703 | @@ -681,6 +704,54 @@ void BitcoinGUI::openClicked() 704 | } 705 | } 706 | 707 | +void BitcoinGUI::newWallet() 708 | +{ 709 | + QString file_name = QFileDialog::getSaveFileName(this, tr("New Wallet"));
This still defaults to the directory that qt is running in. Can we change that to the wallet directory?
719 | + } 720 | +} 721 | + 722 | +void BitcoinGUI::openWallet() 723 | +{ 724 | + QString file_name = QFileDialog::getExistingDirectory(this, tr("Open Wallet"), QString(), QFileDialog::DontResolveSymlinks);
Why is this getExistingDirectory()? A wallet can also be a file I think.
Also defaults to the directory that qt is running in.
78 | @@ -79,6 +79,74 @@ std::shared_ptr<CWallet> GetWallet(const std::string& name) 79 | return nullptr; 80 | } 81 | 82 | +static void GetWalletNameAndPath(const std::string& wallet_file, std::string& wallet_name, fs::path& wallet_path) {
I find this function quite inscrutable. Can you add some comments?
Sure!
Concept ACK, I read through the changes and manually tested the GUI. Ditto to @jnewbery's comments
Removing from High-priority due to conflicts
I suggest adding a warning, once a user has selected a wallet file to open, that you should never ever use wallet files downloaded from strangers.
yes, I have some changes to submit.
Great! Looking forward to seeing them :grin:
in this PR?
No, but worth creating an up for grabs issue for.
@promag - it'd be great to get this in for v0.18. It's the last piece in https://github.com/bitcoin/bitcoin/projects/2.
Longer term, one of my motivations for #13059 was to have bitcoind/qt not create a default wallet at startup if one didn't exist. This PR is the final blocker for that to happen.
Related #11485 (comment)
<!--e57a25ab6845829454e8d69fc972939a-->Reviewers, this pull request conflicts with the following ones:
If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.
This PR does not compile after rebasing on master :-)
@jnewbery wrote:
Longer term [...] to have bitcoind/qt not create a default wallet at startup if one didn't exist. This PR is the final blocker for that to happen.
That would be nice indeed. I find it quite annoying that you can't start bitcoind without any wallet loaded. And I have too many wallet.dat files floating around that I need to carefully check are actually safe to delete :-)
I'll test again after rebase.
Re #11485 listing wallets, this could be added later. It's nice to just show a list of wallet names that can be loaded, avoiding the file dialog.
For this PR it should be enough if the file dialog opens in walletdir, or datadir if that doesn't exist. (the latter is impossible now, so maybe a source code comment warning would suffice).
Creating a new wallet should probably create a data and/or wallet dir, though it's easier if #13761 is merged first.
@Sjors @jnewbery this is what it looks like:
<img width="425" alt="screen shot 2018-09-25 at 12 43 04" src="https://user-images.githubusercontent.com/3534524/46017109-9925fe00-c0ce-11e8-8635-ace219b7f603.png">
For this PR it should be enough if the file dialog opens in walletdir, or datadir if that doesn't exist.
If we handle wallets in the -walletdir differently, then, for external wallets, I think we should point the file dialog to QDesktopServices::DocumentsLocation or whatever.
That looks pretty nice. I'd move Open External Wallet to the bottom and just call it Other.... Indeed it makes sense that doesn't use walletdir.
Doesn't build on linux/win gitian:
CXXLD test/test_bitcoin.exe
AR qt/libbitcoinqt.a
OBJCXXLD qt/bitcoin-qt.exe
libbitcoin_server.a(libbitcoin_server_a-node.o): In function `getWalletDir':
/home/ubuntu/build/bitcoin/distsrc-i686-w64-mingw32/src/interfaces/node.cpp:226: undefined reference to `GetWalletDir[abi:cxx11]()'
collect2: error: ld returned 1 exit status
Makefile:3904: recipe for target 'qt/bitcoin-qt.exe' failed
make[2]: *** [qt/bitcoin-qt.exe] Error 1
make[2]: Leaving directory '/home/ubuntu/build/bitcoin/distsrc-i686-w64-mingw32/src'
Makefile:10188: recipe for target 'all-recursive' failed
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory '/home/ubuntu/build/bitcoin/distsrc-i686-w64-mingw32/src'
Makefile:774: recipe for target 'all-recursive' failed
make: *** [all-recursive] Error 1
Failed run an application inside container
AR qt/libbitcoinqt.a
OBJCXXLD qt/bitcoin-qt
libbitcoin_server.a(libbitcoin_server_a-node.o): In function `interfaces::(anonymous namespace)::NodeImpl::getWalletDir()':
/home/ubuntu/build/bitcoin/distsrc-i686-pc-linux-gnu/src/interfaces/node.cpp:226: undefined reference to `GetWalletDir[abi:cxx11]()'
collect2: error: ld returned 1 exit status
Makefile:3904: recipe for target 'qt/bitcoin-qt' failed
make[2]: *** [qt/bitcoin-qt] Error 1
make[2]: Leaving directory '/home/ubuntu/build/bitcoin/distsrc-i686-pc-linux-gnu/src'
Makefile:10188: recipe for target 'all-recursive' failed
make[1]: *** [all-recursive] Error 1
make[1]: Leaving directory '/home/ubuntu/build/bitcoin/distsrc-i686-pc-linux-gnu/src'
Makefile:774: recipe for target 'all-recursive' failed
make: *** [all-recursive] Error 1
Failed run an application inside container
<!--cf906140f33d8803c4a75a2196329ecb-->Needs rebase
It'd be nice to get this in to 0.18, is that feasible? Needs a rebase
Overall this is looking good, concept ACK with some code review, I'll review more once rebased/the comments above have been addressed and the WIP fixup commit is done
331 | @@ -330,6 +332,15 @@ void BitcoinGUI::createActions() 332 | openAction = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("Open &URI..."), this); 333 | openAction->setStatusTip(tr("Open a bitcoin: URI or payment request")); 334 | 335 | + m_new_wallet_action = new QAction(platformStyle->TextColorIcon(":/icons/open"), tr("New &Wallet"), this);
If we're going to use icons, I think they ought to be different...
Why is there a separate File and Wallet menu in the screenshot above? They should be the same menu...
@promag: care to rebase/pick this up again?
A lot of this has been moved to smaller pull requests. E.g. there's now a separate PR for just the open wallet feature #15153. I suppose "unloading" a wallet would be fairly trivial on top of that.
For my own work on #14912 I'd love to have a (proof of concept) "create wallet" PR to build on top of. Ideally it would a dialog, with a check box for the read-only feature (and later for the empty wallet feature in #14938).
Milestone
0.18.0