RPC refactoring: Access wallet using new GetWalletForJSONRPCRequest #8775

Open
wants to merge 9 commits into
from

Projects

In progress in Multiwallet support

9 participants

@luke-jr
Member
luke-jr commented Sep 21, 2016

Part of the refactorings needed for basic multiwallet (#8694)

@jonasschnelli
Member

I don't like the coupling and the #ifdef ENABLE_WALLET in rpc/server.cpp|.h.
I'd recommend to keep the CRPCRequestInfo wallet-free.

I think we should pack the request path (URI) into the CRPCRequestInfo and or informations about the authentication (in case we want to distinct wallets based on authentication).
Then I think there should be a method in wallet.cpp (or in rpcwallet.cpp) that maps a CWallet * pointer from a given URI, Auth-Info or the complete CRPCRequestInfo instance.

Instead of the CWallet *& pwallet = reqinfo.wallet; there could be then something like CWallet *pwallet = CWallets::getWalletFromRequest(reqinfo)

@jonasschnelli
Member

General ConceptACK on a CRPCRequestInfo for the RPC table commands.
Maybe it could also include the UniValue params and fHelp?

src/rpc/misc.cpp
@@ -70,7 +70,8 @@ UniValue getinfo(const UniValue& params, bool fHelp)
);
#ifdef ENABLE_WALLET
- LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL);
+ CWallet *& pwallet = reqinfo.wallet;
@luke-jr
luke-jr Sep 21, 2016 Member

Yes, pwallet is an alias to reqinfo.wallet which is of type CWallet*.

@luke-jr
Member
luke-jr commented Sep 21, 2016

I think we should pack the request path (URI) into the CRPCRequestInfo and or informations about the authentication (in case we want to distinct wallets based on authentication).
Then I think there should be a method in wallet.cpp (or in rpcwallet.cpp) that maps a CWallet * pointer from a given URI, Auth-Info or the complete CRPCRequestInfo instance.

That sounds nice, but greatly complicates the implementation. :(

@MarcoFalke
Member

I think this can be closed after #8788?

@jonasschnelli
Member

Closing in favor of merged #8788

@luke-jr luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Oct 20, 2016
@luke-jr luke-jr RPC: Allow function signature to include CWallet reference
Wrapped in CRPCRequestInfo to avoid gratuitous #ifdef ENABLE_WALLET everywhere

Github-Pull: #8775
Rebased-From: 80f4ab7
b65bb4e
@luke-jr luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Oct 20, 2016
@luke-jr luke-jr RPC/Wallet: Access wallet via CRPCRequestInfo only
Github-Pull: #8775
Rebased-From: 9279b51
cb370e3
@luke-jr luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Oct 20, 2016
@luke-jr luke-jr Move nWalletUnlockTime to CWallet::nRelockTime, and name timed task u…
…nique per CWallet

Github-Pull: #8775
Rebased-From: 041f4e0
06e4c90
@laanwj laanwj reopened this Oct 25, 2016
@luke-jr
Member
luke-jr commented Oct 25, 2016

Rebased and refactored based on @jonasschnelli 's idea.

@laanwj
Member
laanwj commented Oct 25, 2016

Makes sense, utACK ab5ce98

src/rpc/misc.cpp
@@ -26,6 +26,8 @@
using namespace std;
+CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest&);
@laanwj
laanwj Oct 25, 2016 Member

Not sure how this can pass the build w/ --disable-wallet, but this should be bracketed with #ifdef ENABLE_WALLET

@luke-jr
Member
luke-jr commented Nov 12, 2016

Rebased and addressed nit

@luke-jr luke-jr changed the title from RPC refactoring: Never access wallet directly, only via new CRPCRequestInfo to RPC refactoring: Access wallet using new GetWalletForJSONRPCRequest Nov 12, 2016
@luke-jr luke-jr added a commit to bitcoinknots/bitcoin that referenced this pull request Dec 31, 2016
@luke-jr luke-jr RPC: Pass on CRPCRequestInfo metadata (wallet) for "help" method
Github-Pull: #8775
Similar-To: 7de5573
41383de
@instagibbs
Contributor

utACK 7de5573

@luke-jr
Member
luke-jr commented Jan 5, 2017

Minor change: Forward-declared CWallet even for non-wallet builds so it can be used in a pointer type, avoiding unnecessary casting.

src/rpc/rawtransaction.cpp
@@ -35,6 +35,11 @@
using namespace std;
+#ifdef ENABLE_WALLET
+std::string HelpRequiringPassphrase(CWallet *);
@TheBlueMatt
TheBlueMatt Jan 6, 2017 Contributor

I'd kinda prefer we add another declaration outside of a header in an unrelated file...can we keep it in server.h and ifdef ENABLE_WALLET it?

@luke-jr
luke-jr Jan 6, 2017 Member

I suppose we could. But then it's more likely to get used in new code (which I think we want to discourage?)

@TheBlueMatt
TheBlueMatt Jan 6, 2017 Contributor

I'd think we can (and, more importantly, would) still nag people submitting PRs which add more uses of it outside of src/wallet/rpc*.

src/rpc/rawtransaction.cpp
@@ -35,6 +35,11 @@
using namespace std;
+#ifdef ENABLE_WALLET
+std::string HelpRequiringPassphrase(CWallet *);
@TheBlueMatt
TheBlueMatt Jan 6, 2017 Contributor

I'd think we can (and, more importantly, would) still nag people submitting PRs which add more uses of it outside of src/wallet/rpc*.

src/rpc/misc.cpp
@@ -26,6 +26,10 @@
using namespace std;
+#ifdef ENABLE_WALLET
+CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest&);
@TheBlueMatt
TheBlueMatt Jan 6, 2017 Contributor

Here as well.

src/wallet/rpcdump.cpp
@@ -29,6 +29,7 @@
using namespace std;
+CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest&);
void EnsureWalletIsUnlocked(CWallet *);
bool EnsureWalletIsAvailable(CWallet *, bool avoidException);
@TheBlueMatt
TheBlueMatt Jan 6, 2017 Contributor

Can we move these to some rpc.h in src/wallet? Why are we declaring functions in another file from the definition if they're both in src/wallet???

src/wallet/rpcwallet.cpp
@@ -30,6 +30,11 @@ using namespace std;
int64_t nWalletUnlockTime;
static CCriticalSection cs_nWalletUnlockTime;
+CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request)
@TheBlueMatt
TheBlueMatt Jan 6, 2017 Contributor

Can you document this function somewhere (also probably move its definition to src/wallet/rpcwallet.h) - a few places in src/wallet assume it always returns non-NULL but this is not documented (I havent looked at the actual multi-wallet PR, but does this function then throw an RPC exception if you ask for a wallet that isnt loaded, or does it return NULL then)?

@gmaxwell
Member
gmaxwell commented Jan 7, 2017 edited

So in some cases in the RPC you get the wallet pointer from json but then the check if it's available is far below. This is begging for someone to insert code that uses a potentially null pointer between to two and doesn't notice because their function doesn't get tested with a missing wallet. I would recommend moving the creation of that local pointer down to the point where you're going to test it.

Alternatively or in addition, rename GetWalletForJSONRPCRequest to TryGetWalletForJSONRPCRequest and make GetWalletForJSONRPCRequest a wrapper for it that throws when it fails?

Other than this nit that perhaps getting the pointer and testing it are too separated in some cases, utACK.

@luke-jr
Member
luke-jr commented Jan 7, 2017

I liked the TryGetWalletForJSONRPCRequest/GetWalletForJSONRPCRequest refactor idea, but I don't see any clean way to do it without changing the help behaviours.

So I moved the one case EnsureWalletIsAvailable was delayed up, and removed the blank line between them and GetWalletForJSONRPCRequest.

@TheBlueMatt

Please do not tag the 4th commit "Trivial". We usually define trivial as doesnt touch any code.

As for the 6th commit: please do not do this. nothing in src/wallet is built when ENABLE_WALLET is off, so generally src/wallet/* should never be included outside of src/wallet/*

Aside from the (partial-revert) of "Move wallet RPC declarations to rpcwallet.h" and the printing of the raw pointers to debug log, this looks good to me at d9aff6e.

-void ImportAddress(const CBitcoinAddress& address, const string& strLabel);
-void ImportScript(const CScript& script, const string& strLabel, bool isRedeemScript)
+void ImportAddress(CWallet*, const CBitcoinAddress& address, const string& strLabel);
+void ImportScript(CWallet * const pwallet, const CScript& script, const string& strLabel, bool isRedeemScript)
@TheBlueMatt
TheBlueMatt Jan 7, 2017 Contributor

Note that there are a ton of uses of pwalletMain in ImportScript after the first commit ("RPC/Wallet: Pass CWallet as pointer to helper functions") which are fixed in the next ("RPC: Do all wallet access through new GetWalletForJSONRPCRequest") when they belong in the first.

src/wallet/rpcwallet.cpp
- nWalletUnlockTime = GetTime() + nSleepTime;
- RPCRunLater("lockwallet", boost::bind(LockWallet, pwallet), nSleepTime);
+ pwallet->nRelockTime = GetTime() + nSleepTime;
+ RPCRunLater(strprintf("lockwallet_%u", uintptr_t(pwallet)), boost::bind(LockWallet, pwallet), nSleepTime);
@TheBlueMatt
TheBlueMatt Jan 7, 2017 Contributor

I super dont like the fact that the pointer to the wallet will end up in debug.log if you have debug=rpc enabled.

src/wallet/rpcwallet.cpp
@@ -340,8 +349,7 @@ UniValue getaddressesbyaccount(const JSONRPCRequest& request)
// Find all addresses that have the given account
UniValue ret(UniValue::VARR);
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwallet->mapAddressBook)
- {
+ for (const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item : pwallet->mapAddressBook) {
@TheBlueMatt
TheBlueMatt Jan 7, 2017 Contributor

nit: I believe you can remove the PAIRTYPE and replace with std::pair since we're not using BOOST_FOREACH now.

@@ -1126,7 +1129,7 @@ struct tallyitem
}
};
-UniValue ListReceived(const UniValue& params, bool fByAccounts)
+UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByAccounts)
@TheBlueMatt
TheBlueMatt Jan 7, 2017 Contributor

Note that there are a ton of uses of pwalletMain in ListReceived after the first commit ("RPC/Wallet: Pass CWallet as pointer to helper functions") which are fixed in the next ("RPC: Do all wallet access through new GetWalletForJSONRPCRequest") when they belong in the first.

src/wallet/rpcwallet.cpp
@@ -1219,8 +1241,7 @@ UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bool fByA
// Reply
UniValue ret(UniValue::VARR);
map<string, tallyitem> mapAccountTally;
- BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwallet->mapAddressBook)
- {
+ for (const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item : pwallet->mapAddressBook) {
@TheBlueMatt
TheBlueMatt Jan 7, 2017 Contributor

nit: Here as well (no PAIRTYPE).

src/wallet/rpcwallet.cpp
@@ -1635,14 +1665,14 @@ UniValue listaccounts(const JSONRPCRequest& request)
includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY;
map<string, CAmount> mapAccountBalances;
- BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwallet->mapAddressBook) {
- if (IsMine(*pwallet, entry.first) & includeWatchonly) // This address belongs to me
+ for (const PAIRTYPE(CTxDestination, CAddressBookData)& entry : pwallet->mapAddressBook) {
@TheBlueMatt
TheBlueMatt Jan 7, 2017 Contributor

nit: Here as well (no PAIRTYPE).

@gmaxwell
Member
gmaxwell commented Jan 8, 2017

@luke-jr Looks good to me, will test as soon as you update for matt's latest comments.

@luke-jr
Member
luke-jr commented Jan 8, 2017

Made changes requested by @TheBlueMatt, and rebased to resolve conflict. Also includes a quick sanity check that -wallet doesn't include path separators.

@TheBlueMatt
Contributor
TheBlueMatt commented Jan 9, 2017 edited

While you're at it, can you call SanitizeString (in addition to the "/\" check) on the wallet param?

@TheBlueMatt
Contributor

utACK 7d1228b

@gmaxwell
Member

Needs rebase.

@gmaxwell
Member

ACK.

@fanquake
Contributor

Needs rebasing again.

@luke-jr
Member
luke-jr commented Jan 12, 2017

Rebased yet again.

@gmaxwell
Member

still ACK

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