Wallet/RPC: sweepprivkeys method to scan UTXO set and send to local wallet #9152

Open
wants to merge 6 commits into
from
View
@@ -9,6 +9,8 @@
#include <assert.h>
+#include <stdexcept>
+
/**
* calculate number of bytes for the bitmask, and its number of non-zero bytes
* each bit in the bitmask represents the availability of one output, but the
@@ -47,6 +49,36 @@ uint256 CCoinsView::GetBestBlock() const { return uint256(); }
bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) { return false; }
CCoinsViewCursor *CCoinsView::Cursor() const { return 0; }
+void CCoinsView::FindScriptPubKey(CCoinsViewCursor& cursor, const std::set<CScript>& setscriptNeedles, std::map<uint256, CCoins>& outResults) {
+ for ( ; cursor.Valid(); cursor.Next()) {
+ uint256 hash;
+ CCoins coins;
+ if (!cursor.GetKey(hash)) {
+ throw std::runtime_error(std::string(__func__) + ": GetKey failed");
+ } else if (!cursor.GetValue(coins)) {
+ throw std::runtime_error(std::string(__func__) + ": GetValue failed");
+ }
+ bool fFoundAny = false;
+ for (CTxOut& txo : coins.vout) {
+ if (setscriptNeedles.find(txo.scriptPubKey) == setscriptNeedles.end()) {
+ txo.SetNull();
+ } else {
+ fFoundAny = true;
+ }
+ }
+ if (!fFoundAny) {
+ continue;
+ }
+ coins.Cleanup();
+ outResults.insert(std::pair<uint256, CCoins>(hash, coins));
+ }
+}
+
+void CCoinsView::FindScriptPubKey(const std::set<CScript>& setscriptNeedles, std::map<uint256, CCoins>& outResults) {
+ std::unique_ptr<CCoinsViewCursor> pcursor(Cursor());
+ FindScriptPubKey(*pcursor, setscriptNeedles, outResults);
+}
+
CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
View
@@ -16,6 +16,9 @@
#include <assert.h>
#include <stdint.h>
+#include <map>
+#include <set>
+
#include <boost/foreach.hpp>
#include <boost/unordered_map.hpp>
@@ -316,6 +319,10 @@ class CCoinsView
//! Retrieve the block hash whose state this CCoinsView currently represents
virtual uint256 GetBestBlock() const;
+ //! Search for a given set of pubkey scripts
+ static void FindScriptPubKey(CCoinsViewCursor& cursor, const std::set<CScript>& setscriptNeedles, std::map<uint256, CCoins>& outResults);
+ void FindScriptPubKey(const std::set<CScript>& setscriptNeedles, std::map<uint256, CCoins>& outResults);
+
//! Do a bulk modification (multiple CCoins changes + BestBlock change).
//! The passed mapCoins can be modified.
virtual bool BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock);
View
@@ -72,6 +72,7 @@ const QStringList historyFilter = QStringList()
<< "importmulti"
<< "signmessagewithprivkey"
<< "signrawtransaction"
+ << "sweepprivkeys"
<< "walletpassphrase"
<< "walletpassphrasechange"
<< "encryptwallet";
View
@@ -72,6 +72,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "sendmany", 1, "amounts" },
{ "sendmany", 2, "minconf" },
{ "sendmany", 4, "subtractfeefrom" },
+ { "sweepprivkeys", 0, "options" },
{ "addmultisigaddress", 0, "nrequired" },
{ "addmultisigaddress", 1, "keys" },
{ "createmultisig", 0, "nrequired" },
View
@@ -61,7 +61,9 @@ struct CDiskTxPos : public CDiskBlockPos
}
};
-/** CCoinsView backed by the coin database (chainstate/) */
+/** CCoinsView backed by the coin database (chainstate/)
+ * Cursor requires FlushStateToDisk for consistency.
+ */
class CCoinsViewDB : public CCoinsView
{
protected:
View
@@ -863,6 +863,33 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const
return GetInfo(i);
}
+void CTxMemPool::FindScriptPubKey(const std::set<CScript>& setscriptNeedles, std::map<uint256, CCoins>& outResults) {
+ LOCK(cs);
+ for (const CTxMemPoolEntry& entry : mapTx) {
+ const CTransaction& tx = entry.GetTx();
+ bool fFoundAny = false;
+ for (const CTxOut& txo : tx.vout) {
+ if (setscriptNeedles.find(txo.scriptPubKey) != setscriptNeedles.end()) {
+ fFoundAny = true;
+ break;
+ }
+ }
+ if (!fFoundAny) {
+ continue;
+ }
+
+ const uint256& hash = tx.GetHash();
+ CCoins coins(tx, MEMPOOL_HEIGHT);
+ for (CTxOut& txo : coins.vout) {
+ if (setscriptNeedles.find(txo.scriptPubKey) == setscriptNeedles.end()) {
+ txo.SetNull();
+ }
+ }
+ coins.Cleanup();
+ outResults.insert(std::pair<uint256, CCoins>(hash, coins));
+ }
+}
+
CFeeRate CTxMemPool::estimateFee(int nBlocks) const
{
LOCK(cs);
View
@@ -6,6 +6,7 @@
#ifndef BITCOIN_TXMEMPOOL_H
#define BITCOIN_TXMEMPOOL_H
+#include <map>
#include <memory>
#include <set>
#include <map>
@@ -29,6 +30,7 @@
class CAutoFile;
class CBlockIndex;
+class CScript;
inline double AllowFreeThreshold()
{
@@ -639,6 +641,8 @@ class CTxMemPool
TxMempoolInfo info(const uint256& hash) const;
std::vector<TxMempoolInfo> infoAll() const;
+ void FindScriptPubKey(const std::set<CScript>& setscriptNeedles, std::map<uint256, CCoins>& outResults);
+
/** Estimate fee rate needed to get into the next nBlocks
* If no answer can be given at nBlocks, return an estimate
* at the lowest number of blocks where one can be given
@@ -708,6 +712,7 @@ class CTxMemPool
/**
* CCoinsView that brings transactions from a memorypool into view.
* It does not check for spendings by memory pool transactions.
+ * Its Cursor also doesn't work. In general, it is broken as a CCoinsView implementation outside of a few use cases.
*/
class CCoinsViewMemPool : public CCoinsViewBacked
{
View
@@ -75,6 +75,8 @@ std::string DecodeDumpString(const std::string &str) {
return ret.str();
}
+void ParseWIFPrivKey(const std::string strSecret, CKey& key, CPubKey& pubkey);
+
UniValue importprivkey(const JSONRPCRequest& request)
{
if (!EnsureWalletIsAvailable(request.fHelp))
@@ -118,16 +120,9 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (fRescan && fPruneMode)
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strSecret);
-
- if (!fGood) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
-
- CKey key = vchSecret.GetKey();
- if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
-
- CPubKey pubkey = key.GetPubKey();
- assert(key.VerifyPubKey(pubkey));
+ CKey key;
+ CPubKey pubkey;
+ ParseWIFPrivKey(strSecret, key, pubkey);
CKeyID vchAddress = pubkey.GetID();
{
pwalletMain->MarkDirty();
@@ -752,21 +747,9 @@ UniValue processImport(const UniValue& data) {
for (size_t i = 0; i < keys.size(); i++) {
const string& privkey = keys[i].get_str();
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(privkey);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CKey key = vchSecret.GetKey();
-
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
- }
-
- CPubKey pubkey = key.GetPubKey();
- assert(key.VerifyPubKey(pubkey));
+ CKey key;
+ CPubKey pubkey;
+ ParseWIFPrivKey(privkey, key, pubkey);
CKeyID vchAddress = pubkey.GetID();
pwalletMain->MarkDirty();
@@ -862,21 +845,9 @@ UniValue processImport(const UniValue& data) {
if (keys.size()) {
const string& strPrivkey = keys[0].get_str();
- // Checks.
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(strPrivkey);
-
- if (!fGood) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
- }
-
- CKey key = vchSecret.GetKey();
- if (!key.IsValid()) {
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range");
- }
-
- CPubKey pubKey = key.GetPubKey();
- assert(key.VerifyPubKey(pubKey));
+ CKey key;
+ CPubKey pubKey;
+ ParseWIFPrivKey(strPrivkey, key, pubKey);
CBitcoinAddress pubKeyAddress = CBitcoinAddress(pubKey.GetID());
Oops, something went wrong.