-
Notifications
You must be signed in to change notification settings - Fork 37.7k
rpc: Added ability to remove watch only addresses #15129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
rpc: Added ability to remove watch only addresses #15129
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add some tests.
ba80d4f
to
685f00f
Compare
aeffaea
to
a3a1233
Compare
Not opposed against removing scripts. But maybe combining with |
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, 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. |
0f6e428
to
f49c523
Compare
Added functionality to remove scripts as well |
Looks much better! |
@benthecarman could you address Travis linter failure? |
7533c58
to
4c4a43f
Compare
Added release notes |
4c4a43f
to
0e4483e
Compare
Rebased and fixed release notes |
Could trailing whitespaces be removed? |
0e4483e
to
4c7d2b6
Compare
@hebasto Sorry about that, fixed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concept ACK
It looks to me there is no test for |
Added |
723aed1
to
2eb5021
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ACK 2eb5021
reACK 2eb5021 |
"\nRemove a script\n" + HelpExampleCli("removeaddress", "\"myscript\"") + | ||
"\nAs a JSON-RPC call\n" + HelpExampleRpc("removeaddress", "\"myaddress\"")}, | ||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { | ||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the body of RPC functions shouldn't be indented for the RPCHelpMan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say it is allowed for new code to use the right whitespace
src/wallet/rpcdump.cpp
Outdated
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { | ||
std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); | ||
if (!wallet) return NullUniValue; | ||
CWallet& pwallet = *wallet.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just use the shared_ptr everywhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I fixed this
test/functional/wallet_basic.py
Outdated
@@ -504,15 +504,58 @@ def run_test(self): | |||
{"address": address_to_import}, | |||
{"spendable": False}) | |||
|
|||
# 5. Import private key of the previously imported address on node1 | |||
# 5. Remove the address from node1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I don't think we need the numbering (makes it annoying to add more cases later)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed (this was very annoying)
# 5. Import private key of the previously imported address on node1 | ||
# 5. Remove the address from node1 | ||
self.nodes[1].removeaddress(address_to_import, False, False) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be good to validate here that the address is not in the wallet (like below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added
# Check the address was removed and other address is unaffected | ||
assert not self.nodes[1].getaddressinfo(address_to_import2)["iswatchonly"] | ||
assert self.nodes[1].getaddressinfo(address_to_import)["ismine"] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to test the p2sh
flag as well, all these examples only touch addresses not scripts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
Thank you for maintaining this PR @benthecarman, it is definitely getting close! |
2eb5021
to
b8eb588
Compare
Responded to @meshcollider's review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code review almost-ack b8eb588 modulo my review comments marked "Important:" in the RemoveScript function, which should be easy to address with small changes.
} | ||
} | ||
|
||
if (txContainsScript && !IsFromMe(*wTx.tx) && !IsMine(*wTx.tx)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: Added ability to remove watch only addresses" (b8eb588)
Note (no changes requested): Check for isfromme / ismine mirrors existing logic in AddToWalletIfInvolvingMe and seems sufficient. I was initially thinking the IsFromMe check might fail to remove all relevant transactions because the for loop here ignores dependencies between transactions, so if IsFromMe was called on a child transaction and the parent was only removed later, the child might fail to be removed. But I think this not a problem because the PR only supports moving watch-only addresses, not spendable addresses, so no IsFromMe result on any transaction will be changed by watch only address removal.
"\nRemoves an address or script (in hex) that was being watched as if it were in your wallet but was not being used to spend. Requires a new wallet backup.\n", | ||
{ | ||
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The Bitcoin address (or hex-encoded script)"}, | ||
{"p2sh", RPCArg::Type::BOOL, RPCArg::Default{false}, "Whether to remove the P2SH version of the script as well"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: Added ability to remove watch only addresses" (b8eb588)
This documentation is mirrored from the importaddress
documentation and it is good that you are keeping it consistent, but I think it is actually a little confusing and would be nice to clarify both places. I would maybe change the help string here to something like "If this is true and a script is being removed (not an address, if an address is being removed this is required to be false), then treat the script as a P2SH redeem script and remove the associated P2SH address from the wallet as well."
For importaddress
you could just use the same help text just replacing "remove[d]" with "add[ed]".
It might be also be helpful to add a general note about the limitations of this RPC like "This RPC method does not currently support removing P2WSH or P2SH-P2WSH witness scripts at the same time as their associated addresses, the same way it does support this P2SH redeem scripts, so separate calls must be made to do this, first removing the addresses, and then removing the witness scripts."
{"p2sh", RPCArg::Type::BOOL, /* default */ "false", "Remove the P2SH version of the script as well"}, | ||
{"purgetransactions", RPCArg::Type::BOOL, /* default_val */ "false", "Remove existing transactions from the wallet"}, | ||
}, | ||
RPCResult{RPCResult::Type::NONE, "", ""}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should be returned?
It does seem ok to me to return null like this PR is currently doing. But if you wanted to extend it you could return lists of scripts and addresses removed by the recursive removescript/removeaddress implementation (maybe in a similar format similar to the importmulti requests parameter). There could also be a "warnings" field for example if you remove a known P2SH redeem script without removing the associated address.
if (is_redeem_script) { | ||
CScriptID scriptID = CScriptID(script); | ||
if (!spk_man.HaveCScript(scriptID)) { | ||
throw JSONRPCError(RPC_WALLET_ERROR, "Error removing p2sh redeemScript from wallet"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: Added ability to remove watch only addresses" (b8eb588)
Important: It seems dangerous to do this error check and raise an error after calling PurgeWatchOnly above. It would be better to check for this error condition early so the remove either fully fails or fully succeeds instead of half removing the redeem script.
} | ||
if (is_redeem_script) { | ||
CScriptID scriptID = CScriptID(script); | ||
if (!spk_man.HaveCScript(scriptID)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: Added ability to remove watch only addresses" (b8eb588)
Important: Because there is no RemoveCScript function being called here to undo the effect of AddCScript/AddCScriptWithDB, the removeaddress
call does not fully undo the effects of the equivalent importaddress
call when the p2sh
argument is true, and a copy of the script is still left behind in mapScripts
and in the database file. It would be good to either note this as a limitation of the RPC in the documentation, or to fix it by actually removing the script and scriptid from the database.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re: #15129 (comment)
a copy of the script is still left behind in
mapScripts
and in the database file
Might be good follow up with @achow101 about his earlier comment here #15129 (comment) since I'm assuming leaving data behind in mapscripts is wasteful but doesn't cause real problems, but his comment seems to imply otherwise.
Also on Andrew's other comment about the keypool, #15129 (comment), I think you could address it again with documentation just saying removeaddress
only undoes the effects of importaddress
, it doesn't fully undo the effects of importmulti
if the address was imported with other data like public keys.
if (!spk_man.HaveCScript(scriptID)) { | ||
throw JSONRPCError(RPC_WALLET_ERROR, "Error removing p2sh redeemScript from wallet"); | ||
} | ||
RemoveAddress(pwallet, ScriptHash(scriptID), purge_txns); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In commit "rpc: Added ability to remove watch only addresses" (b8eb588)
Important: Again because this can throw it seems dangerous to call this here after PurgeWatchOnly has run and succeeded. Would be better to call PurgeWatchOnly after this so the RPC either completely fails or completely succeeds instead of partially succeeding.
🐙 This pull request conflicts with the target branch and needs rebase. Want to unsubscribe from rebase notifications on this pull request? Just convert this pull request to a "draft". |
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { | ||
const CWalletTx& wTx = it->second; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { | |
const CWalletTx& wTx = it->second; | |
for (auto& [txid, wTx] : mapWallet) { |
} | ||
|
||
for (const auto& wtx : vWtx) { | ||
mapWallet.erase(wtx->GetHash()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At the very least, we need to delete it from wtxOrdered
too - and probably mapTxSpends
(see CWallet::ZapSelectTx
- maybe we should abstract this to a new method). Ubuntu hirsute (newer libc?) crashes in the tests due to a use-after-free trying to iterate wtxOrdered after removeaddress; I'm not sure if there's a good way to generalise this test to detect the problem on older systems.
Not sure why, but simply doing wtxOrdered.erase(wtx->m_it_wtxOrdered);
doesn't appear to fix it :/
@@ -493,26 +493,90 @@ def run_test(self): | |||
assert_raises_rpc_error(-8, 'Invalid estimate_mode parameter, must be one of: "unset", "economical", "conservative"', | |||
self.nodes[2].sendtoaddress, address=address, amount=1, conf_target=target, estimate_mode=mode) | |||
|
|||
# 2. Import address from node2 to node1 | |||
# Import address from node2 to node1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels like we should have more self.log.info
calls - is it still really about "sendtoaddress raises if an invalid fee_rate is passed" here?
Can this be extended to descriptors? |
There hasn't been much activity lately and the patch still needs rebase. What is the status here?
|
Closing as this has needed rebase for more than 1 year. Feel free to reopen if you get a chance to work on this again in the future, thanks! |
@benthecarman You don't plan to continue work on this? If not, I could take over. |
Sorry I don't have time to work on this at the moment. Feel free to take it over |
I added a new rpc call that allows you to remove a watch only address
Format:
removeaddress address/script (p2sh) (purgetransactions)