Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Basic Multiwallet GUI support #11383

Open
wants to merge 9 commits into
from

Conversation

Member

luke-jr commented Sep 21, 2017

This adds a combobox to the GUI main window and debug window to select the wallet being viewed/used.

src/qt/bitcoin.cpp
- window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel);
- window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET);
+ QString WalletName = QString::fromStdString(pwallet->GetName());
+ if (WalletName.endsWith(".dat")) {
@jonasschnelli

jonasschnelli Sep 22, 2017

Member

Hmm.. not sure if we should hardcode .dat at this point.

@luke-jr

luke-jr Sep 22, 2017

Member

It's strictly for stripping.

@@ -462,6 +462,15 @@ void BitcoinGUI::createToolBars()
toolbar->addAction(receiveCoinsAction);
toolbar->addAction(historyAction);
overviewAction->setChecked(true);
+
+#ifdef ENABLE_WALLET
@jonasschnelli

jonasschnelli Sep 22, 2017

Member

this #ifdef could be avoided/removed, right?

Member

jonasschnelli commented Sep 22, 2017

Nice!
Works pretty good (played around with it a bit).
I think this may be a first step.

Conceptual issues:

  • The Combox box without a direct user feedback may lead to selecting the wrong wallet. Android dual/multi-sim works against this by always displaying an icon for the SIM card the action will be executed with. Not 1:1 applicable here... but users should get A) a feedback after switching wallets (are you sure, etc.), B) better visual distinction.
  • Having the HD / encryption status in the status-bar seems not to be the right place when running multiwallet. It is basically on the same level then the QComboBox.
  • Wallet name tied to the filename may be not the best choice. Would something speak against having a dedicated walletname per wallet, .. or would this confuse?

Ideas:

  • Ideally we would have a wallet-navigation-drawer at the left side that consume a more prominent space and make it move obvious which wallet is used.
  • Critical actions like send-coins should maybe remind the user in the confirmation dialog which wallet it will use.
  • Users eventually should choose an (coloured) icon per wallet (give a pre-selection of 12 icons) which then will be always visible on a top window level and or displayed when executing critical actions (showing addresses, sending coins, etc.)

Code:

  • I don't see why the RPC auth argument would be necessary at this point (but I respect your comment that we should discuss that in #10615)
  • I dislike the #Ifdef ENABLE_WALLET clustering. I would reduce it to a minimum and runtime bypass where possible. Especially the #Ifdefs in RPC / http low level code is ugly.
Member

jnewbery commented Oct 5, 2017

I think we can fairly easily pull out the contentious rpcauth parts of this to make it more palatable for review. I have a branch here: https://github.com/jnewbery/bitcoin/tree/pr11383.1 which removes the first three commits and makes the one change to rpcconsole.cpp to make this work without rpcauth. It's also rebased on master since there was a merge conflict.

Member

luke-jr commented Oct 6, 2017

Rebased, eliminated ENABLE_WALLET stuff added to non-wallet/GUI code, and removed the dependency on rpcauth default wallet stuff.

I did not address @jonasschnelli's GUI comments, because I feel some is best explored separately, as improvements on top of this, while others (confirming wallet changes with a prompt) I think would make the feature annoying to use.

src/qt/bitcoin.cpp
@@ -252,7 +252,7 @@ public Q_SLOTS:
QTimer *pollShutdownTimer;
#ifdef ENABLE_WALLET
PaymentServer* paymentServer;
- WalletModel *walletModel;
+ std::vector<WalletModel*> walletModels;
@ryanofsky

ryanofsky Oct 9, 2017

Contributor

In commit "Qt: Load all wallets into WalletModels"

Might be simpler to make this a vector of unique_ptr, to avoid need for manual deletions. Also since renaming this member anyway, could follow current naming convenetion (m_wallet_models).

@luke-jr

luke-jr Oct 12, 2017

Member

Not sure unique_ptr is the right tool for the job here. Renamed.

src/qt/rpcconsole.cpp
+ const int wallet_index = ui->WalletSelector->currentIndex();
+ if (wallet_index > 0) {
+ CWalletRef *ppwallet = (CWalletRef*)ui->WalletSelector->itemData(wallet_index).value<void*>();
+ ppwallet = new CWalletRef(*ppwallet); // Refcount
@ryanofsky

ryanofsky Oct 9, 2017

Contributor

In commit "Qt: Add wallet selector to debug console"

What does refcount comment mean? Is this a todo suggesting that you want to add refcounting in the future? Should clarify comment or maybe remove it.

@luke-jr

luke-jr Oct 12, 2017

Member

Clarified.

@@ -65,6 +65,15 @@ WalletModel::~WalletModel()
unsubscribeFromCoreSignals();
}
+QString WalletModel::getWalletName() const
@ryanofsky

ryanofsky Oct 9, 2017

Contributor

In commit "Qt: When multiple wallets are used, include in notifications the name"

Would be nice to follow this up by removing the name parameter passed to BitcoinGUI::addWallet to avoid duplicating .dat stripping logic.

@@ -95,13 +95,13 @@ void WalletView::setBitcoinGUI(BitcoinGUI *gui)
connect(this, SIGNAL(message(QString,QString,unsigned int)), gui, SLOT(message(QString,QString,unsigned int)));
// Pass through encryption status changed signals
- connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(setEncryptionStatus(int)));
+ connect(this, SIGNAL(encryptionStatusChanged(int)), gui, SLOT(updateWalletStatus()));
@ryanofsky

ryanofsky Oct 9, 2017

Contributor

In commit "Qt: Ensure UI updates only come from the currently selected walletView"

This is silently dropping the int status value passed to encryptionStatusChanged, and the int hdEnabled value passed to hdEnabledStatusChanged. Assuming this is intended, you should clean up after this change by deleting the unused parameters from encryptionStatusChanged and hdEnabledStatusChanged declarations and calls.

-static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
-
-CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
+void JSONRPCRequestWalletResolver(JSONRPCRequest& jreq, const HTTPRequest& httpreq)
@ryanofsky

ryanofsky Oct 9, 2017

Contributor

In commit "RPC: Pass wallet through JSONRPCRequest"

Multiwallet rpc calls are broken in this commit (and the whole PR) because JSONRPCRequestWalletResolver is not actually registered or called anywhere. Maybe a commit was lost in the rebase? The problem causes multiwallet.py test to fail with "Wallet file not specified"

Member

luke-jr commented Oct 12, 2017

(Note Travis failure is due to the batch request bug fixed in #11277)

Member

jnewbery commented Oct 12, 2017

#11277 is merged. Can you rebase?

Member

luke-jr commented Oct 12, 2017

Rebased

@fanquake fanquake added this to In progress in Multiwallet support Nov 4, 2017

luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Nov 6, 2017

luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Nov 6, 2017

@laanwj laanwj added this to Blockers in High-priority for review Nov 16, 2017

Lightly tested ACK: 555eec1

Works as intended.
Visually not very elegant, but an acceptable first step.

/** WWW-Authenticate to present with 401 Unauthorized response */
static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
+boost::signals2::signal<void (JSONRPCRequest&, const HTTPRequest&)> PrepareJSONRPCRequestCallbacks;
@jonasschnelli

jonasschnelli Nov 16, 2017

Member

Isn't the HTTPRequest object unused? Why pack it into the signal, future extensions?

+ PrepareJSONRPCRequestCallbacks.connect(preparer);
+}
+
+void UnregisterJSONRPCRequestPreparer(const JSONRPCRequestPreparer& preparer)
@jonasschnelli

jonasschnelli Nov 16, 2017

Member

This gets never called, right?

@@ -236,6 +251,9 @@ bool StartHTTPRPC()
#ifdef ENABLE_WALLET
// ifdef can be removed once we switch to better endpoint support and API versioning
RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
+
+ void JSONRPCRequestWalletResolver(JSONRPCRequest& jreq, const HTTPRequest& httpreq);
@jonasschnelli

jonasschnelli Nov 16, 2017

Member

use externoutside function scope?

- // in Qt, use always the wallet with index 0 when running with multiple wallets
- QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(vpwallets[0]->GetName()));
- req.URI = "/wallet/"+std::string(encodedName.constData(), encodedName.length());
+ CWalletRef * const ppwallet = (CWalletRef*)ppwallet_v;
@jonasschnelli

jonasschnelli Nov 16, 2017

Member

looks like we should use a C++11 (shared?) pointer here to avoid the manual memory management.

Tested, but will look the code. Works as expected.

There are some style issues that could be addressed here or in follow ups:

  • combo and label in the toolbar use different style (font for instance) than the buttons;
  • not sure if the right side is the best place in terms of UX;
  • maybe it should be possible to select wallet from the menu bar (for instance, File -> Wallets -> w1);
  • window titles could have the wallet name too;
  • the combo in the console is a bit lost (this time is not in a toolbar);
  • could show a message in the console when the wallet is changed.
@@ -45,8 +47,9 @@ class JSONRPCRequest
bool fHelp;
std::string URI;
std::string authUser;
+ CWallet *wallet;
@promag

promag Nov 19, 2017

Contributor

Do we want wallet here even an opaque pointer?

Contributor

Sjors commented Nov 20, 2017

I tested the debug console by switching wallets (including no wallet) and using dumpwallet. This seems to work as expected. I also checked that wallet backups from the application menu use the correct wallet.

I agree with @promag's suggestions and added a few below. I don't think any should block this PR, although some might simplify it.

Maybe this is what you were saying, but I have a light preference for reducing UI clutter by removing the dropdowns and instead adding Wallet to the primary menu (between File and Settings). The application title would contain the name of the currently selected wallet, as you suggested. In the Wallet menu, the selected wallet would have a check box, and not be selectable. This has a couple of advantages:

  • it's more future proof for when there's UI for adding wallets, creating new wallets, etc
  • you can move Backup Wallet there as well (and rename it to Backup [name of wallet])
  • you can move Encrypt and change passphrase there (and get rid of Settings menu)
  • you could open each wallet in its own window (probably not desirable though)
    • it would make it easier for users to send funds between wallets, and be more like opening multiple word documents
    • I don't know if that's possible (maybe it creates a threading nightmare)
    • it makes it more difficult to use the application menu for actions, because you have to clarify which wallet the action refers to (or they need to work nicely with multiple wallets)

I assume #10740 is needed before wallets can be added through the UI?

I like @jonasschnelli's suggestion of allowing each wallet to have a name, so the file name can be inconspicuous. Same for clearly showing the wallet name on the send confirmation screen. I also like the idea of using color, although that can get ugly very quickly.

In case anyone else was confused like me, this how you launch with multiple wallets:
Bitcoin-Qt -wallet=wallet1.dat -wallet=wallet2.dat (and it won't complain if you open a non-existing wallet, e.g. because you left out .dat; it will just create it for you)

@Sjors Sjors referenced this pull request Nov 20, 2017

Open

[wallet] dynamic loading/unloading of wallets #10740

0 of 1 task complete
+
+bool BitcoinGUI::multiWallet() const
+{
+ return (WalletSelector->count() > 1);
@Sjors

Sjors Nov 22, 2017

Contributor

Using a UI element to count the number of wallets doesn't seem ideal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment