Does this look like a good approach?
TODO:
- rawtransaction sweep functionality
- GUI sweep (Receive tab?)
- abstract shared sweep logic
RPC tests
NOTE: Currently needs #14602 for tests to pass
Concept ACK (Haven't really looked at the code). I think a sweep function would be a great feature. One could import "old" private keys into a new HD wallet for example.
Possible extension: sweepseed could be an extended version of that, moving all funds form a HD seed to a new one, generating large lookup-windows on different chainpathes. It could also be UTXO set only not requiring a -rescan.
From a raw flow perspective, the generation of the sweep transaction is something that works from public information and should be possible on an online node without access to the private keys... so that one should be a 'createrawsweeptransaction' which takes a list of adresses/pubkeys/redeemscripts (and maybe private keys ... maybe some kind of BIP32 chain spec) and returns a transaction that spends all coins assigned to matching keys, potentially with arguments to limit the set of inputs collected.
Hmm, what about extending RPC importprivkey with another optional argument sweep defaulting to false?
sweepprivkeys is intended for users, and not to import keys. Users should never use importprivkey.
1070 | + std::map<uint256, CCoins> mapcoins; 1071 | + { 1072 | + LOCK(cs_main); 1073 | + mempool.FindScriptPubKey(setscriptSearch, mapcoins); 1074 | + FlushStateToDisk(); 1075 | + pcoinsTip->FindScriptPubKey(setscriptSearch, mapcoins);
It might be appropriate to loop over mapCoins calling CTxMemPool::pruneSpent, on each entry, to avoid trying to sweep from an output that is already in the process of being spent.
Concept ACK.
Hmm, what about extending RPC importprivkey with another optional argument sweep defaulting to false?
Please don't do this. People confuse import and sweep all over the place. The least we can do is make them separate RPCs with separate documentation.
Seems this won't be making it for 0.15. Untagging.
Concept Re-ACK. Needs rebase. I guess this is also something we could want for 0.16. I have no strong opinion about sweep versus createrawsweep. The approach we took for bumpfee was also to do the non raw one first, although I agree that the ramification of sweep is different then a bump.
Rebased and added a simple functional test.
1105 | + 1106 | + tempKeystore.AddKey(key); 1107 | + CKeyID address = pubkey.GetID(); 1108 | + CScript script = GetScriptForDestination(address); 1109 | + if (!script.empty()) { 1110 | + needles.insert(script);
Throw if duplicate? Add test.
I don't see why a duplicate privkey should be an error.
1109 | + if (!script.empty()) { 1110 | + needles.insert(script); 1111 | + } 1112 | + script = GetScriptForRawPubKey(pubkey); 1113 | + if (!script.empty()) { 1114 | + needles.insert(script);
Throw if duplicate? Add test.
1119 | + } else { 1120 | + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Unrecognised option '%s'", optname)); 1121 | + } 1122 | + } 1123 | + 1124 | + // Ensure keypool is filled if possible
Throw if needles is empty? Add test.
If it's empty, we'll throw "No value to sweep" later on. And so long as the user provides something, it will never be empty.
1132 | + 1133 | + // Reserve the key we will be using 1134 | + CReserveKey reservekey(pwallet); 1135 | + CPubKey pubkey; 1136 | + if (!reservekey.GetReservedKey(pubkey)) { 1137 | + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first");
Add test for this error?
I don't know how, and the two tests currently "testing" it don't make logical sense.
1137 | + throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); 1138 | + } 1139 | + 1140 | + // Scan UTXO set for inputs 1141 | + std::vector<CTxOut> input_txos; 1142 | + {
Unnecessary block?
Scope for coins
1171 | + 1172 | + while (true) { 1173 | + if (IsDust(tx.vout[0], ::minRelayTxFee)) { 1174 | + throw JSONRPCError(RPC_VERIFY_REJECTED, "Swept value would be dust"); 1175 | + } 1176 | + for (size_t input_index = 0; input_index < tx.vin.size(); ++input_index) {
Take this out of fee loop?
Fee changes require resigning. Perhaps it could be made to sign only before and after the loop, but the time here is trivial in comparison to the UTXO search.
Is it me or this could use CWallet::FundTransaction with coin control configured and with subtract fee from amount?
Fixed the test failure (missing cs_main lock on AcceptToMemoryPool)
@luke-jr: can you rebase this once again? Thanks
Rebased
utACK a397deb247fc404064cf76ede8d64eb68f4fefe6 (will test soon). I personally dislike the "direct"-approach. I would like to "see" the transaction before submitting.
Possible extensions:
createrawsweeptransaction (or similar)ACK using a397deb247fc40. This is a great feature many people need. I'm moving on to test it on mainnet using the v0.15.0.knots20170914 build. I encourage inclusion and later on apply extensions (esp what @jonasschnelli recommends above).
0 | @@ -0,0 +1,66 @@ 1 | +#!/usr/bin/env python3 2 | +# Copyright (c) 2014-2017 The Bitcoin Core developers 3 | +# Distributed under the MIT software license, see the accompanying 4 | +# file COPYING or http://www.opensource.org/licenses/mit-license.php. 5 | +"""Test the sweepprivkeys RPC.""" 6 | + 7 | +import decimal 8 | +import math
seems like unused imports?
The check fail is unrelated to this PR and limited to the ARM build, lacking the "flake8" python library.
contrib/devtools/lint-python.sh: 10: contrib/devtools/lint-python.sh: flake8: not found
removing unused imports won't fix lint-python.sh either.
ping and HNY everyone :^P
Since the keys are not added to the wallet, and thus bumpfee won't work, it makes sense to set nSequence to support rbf and return a list of signed raw transactions that each pay a slightly higher fee?
@MarcoFalke I believe yours is a brilliant idea. Just to be clear, you mean a list of signed raw transactions that are ready to replace the one made and can be fired up from the lowest fee to the higher to bump if necessary, right? such transactions should be discarded once enough confirmations are present.
If we still want this, it needs rebase/implementation in terms of #12196. But I'm going to close it for now, it's been open since 2016 and there's no clear progress.
It might make sense to open a new PR if you start work on this again, or you can ask me to reopen it.
Interesting revamp. I have read the whole thread in #12196, is there some documentation being worked about use examples for scantxoutset to be used in place of sweeping? I like to try to sort out my needs using that, but haven't kept myself up to date enough to understand it without handholding. There is the integration test in python, but...
@luke-jr You can only (force) push after it has been reopened. If the branch is different in any way, it won't reopen.
<!--e57a25ab6845829454e8d69fc972939a-->
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
<!--174a7506f384e20aa4161008e828411d-->
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.
942 | + const UniValue& optval = request.params[0][optname]; 943 | + if (optname == "privkeys") { 944 | + const UniValue& privkeys_a = optval.get_array(); 945 | + for (size_t privkey_i = 0; privkey_i < privkeys_a.size(); ++privkey_i) { 946 | + const UniValue& privkey_wif = privkeys_a[privkey_i]; 947 | + std::string wif_secret = privkey_wif.get_str();
Could be const reference?
Concept ACK. Still needs rebase. IMO this should be reworked to take a descriptor which would address the points above about accepting pubkeys/HD keys. Agree that returning the raw transaction is preferable (createrawsweeptransaction)
I'm afraid this PR doesn't build when rebased on master.
<!--cf906140f33d8803c4a75a2196329ecb-->Needs rebase
Concept ACK
This should probably use "scantxoutset" internally (rebase issue).
I also think it should be a createrawsweep call (rather then directly signing and executing).
Maybe you can change this to be an argument for "scantxoutset" that would also spit out a rawtransaction (with configurable fee and eventually configurable target address) for the sweep transaction.
972 | + } 973 | + 974 | + if (request.fHelp || request.params.size() != 1) { 975 | + throw std::runtime_error( 976 | + RPCHelpMan{"sweepprivkeys", 977 | + "\nSends bitcoins controlled by private key to specified destinations.\n",
This says "specified destinations" but you can't specify the destinations. Can you also make it clear that coins are sweeped one by one to unique addresses (per priv key)?
Concept ACK. Using scantxoutset internally seems like a good idea. It needs a fee control option, could be as simple as feeRate with 1 sat/byte default. Ideally also RBF control, but that can wait for a followup.
As mentioned by others, it's good to be able to inspect the transaction. That could just be another option. In #16378 I introduce a add_to_wallet to option, and the RPC call returns a hex & PSBT if false.
Another potential followup could add descriptor support; those can have private keys now too. Others above suggest using those instead of WIF, but I think an array of WIFs is still common enough to be worth supporting directly.
This PR sweeps into the wallet itself. A followup could sweep into any arbitrary descriptor, which would make migrating wallets to some new scheme easier, e.g. moving from single-sig to multi-sig without joining UTXOs.
I don't see any sane way to rebase this post-bitcoin-wallet, so going to have to just make it a Knots-only hack for now. :/
I don't see any sane way to rebase this post-bitcoin-wallet
I don't think it should be too bad if someone wants to pick it up. It should just require a new interfaces::Chain method to wrap FindScriptPubKey calls, since wallet code can no longer access mempool and pcoinsdbview globals directly.
Maintainers maybe remove "Waiting for author" and "Needs rebase" tags and mark "Up for grabs"