Browse Source

Backport importpubkey RPC from CHIPS (BTC 0.15.x)

pull/132/head
Duke Leto 6 years ago
parent
commit
022860159d
  1. 13
      src/chain.h
  2. 1
      src/rpcclient.cpp
  3. 70
      src/rpcmisc.cpp
  4. 3
      src/rpcserver.cpp
  5. 5
      src/script/standard.cpp
  6. 1
      src/script/standard.h
  7. 6
      src/wallet/rpcdump.cpp
  8. 60
      src/wallet/wallet.cpp
  9. 67
      src/wallet/wallet.h

13
src/chain.h

@ -17,6 +17,8 @@
#include <boost/foreach.hpp>
static const int SPROUT_VALUE_VERSION = 1001400;
static const int64_t MAX_FUTURE_BLOCK_TIME = 2 * 60 * 60;
static const int64_t TIMESTAMP_WINDOW = MAX_FUTURE_BLOCK_TIME;
struct CDiskBlockPos
{
@ -167,6 +169,9 @@ public:
//! (memory only) Sequential id assigned to distinguish order in which blocks are received.
uint32_t nSequenceId;
//! (memory only) Maximum nTime in the chain up to and including this block.
unsigned int nTimeMax;
void SetNull()
{
phashBlock = NULL;
@ -256,6 +261,11 @@ public:
return (int64_t)nTime;
}
int64_t GetBlockTimeMax() const
{
return (int64_t)nTimeMax;
}
enum { nMedianTimeSpan=11 };
int64_t GetMedianTimePast() const
@ -441,6 +451,9 @@ public:
/** Find the last common block between this chain and a block index entry. */
const CBlockIndex *FindFork(const CBlockIndex *pindex) const;
/** Find the earliest block with timestamp equal or greater than the given. */
CBlockIndex* FindEarliestAtLeast(int64_t nTime) const;
};
#endif // BITCOIN_CHAIN_H

1
src/rpcclient.cpp

@ -86,6 +86,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "lockunspent", 1 },
{ "importprivkey", 2 },
{ "importaddress", 2 },
{ "importpubkey", 2 },
{ "verifychain", 0 },
{ "verifychain", 1 },
{ "keypoolrefill", 0 },

70
src/rpcmisc.cpp

@ -936,7 +936,77 @@ UniValue getaddresstxids(const UniValue& params, bool fHelp)
}
return result;
}
//void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel);
void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel);
UniValue importpubkey(const UniValue& params, bool fHelp)
{
CWallet * const pwallet = vpwallets[0];
if (!pwallet) {
return NullUniValue;
}
if (fHelp || params.size() < 1 || params.size() > 4)
throw std::runtime_error(
"importpubkey \"pubkey\" ( \"label\" rescan )\n"
"\nAdds a public key (in hex) that can be watched as if it were in your wallet but cannot be used to spend. Requires a new wallet backup.\n"
"\nArguments:\n"
"1. \"pubkey\" (string, required) The hex-encoded public key\n"
"2. \"label\" (string, optional, default=\"\") An optional label\n"
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
"\nNote: This call can take minutes to complete if rescan is true, during that time, other rpc calls\n"
"may report that the imported pubkey exists but related transactions are still missing, leading to temporarily incorrect/bogus balances and unspent outputs until rescan completes.\n"
"\nExamples:\n"
"\nImport a public key with rescan\n"
+ HelpExampleCli("importpubkey", "\"mypubkey\"") +
"\nImport using a label without rescan\n"
+ HelpExampleCli("importpubkey", "\"mypubkey\" \"testing\" false") +
"\nAs a JSON-RPC call\n"
+ HelpExampleRpc("importpubkey", "\"mypubkey\", \"testing\", false")
);
std::string strLabel;
if (!params[1].isNull())
strLabel = params[1].get_str();
// Whether to perform rescan after import
bool fRescan = true;
if (!params[2].isNull())
fRescan = params[2].get_bool();
if (fRescan && fPruneMode)
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
}
if (!IsHex(params[0].get_str()))
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey must be a hex string");
std::vector<unsigned char> data(ParseHex(params[0].get_str()));
CPubKey pubKey(data.begin(), data.end());
if (!pubKey.IsFullyValid())
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Pubkey is not a valid public key");
{
LOCK2(cs_main, pwallet->cs_wallet);
for (const auto& dest : GetAllDestinationsForKey(pubKey)) {
ImportAddress(pwallet, dest, strLabel);
}
//ImportScript(pwallet, GetScriptForRawPubKey(pubKey), strLabel, false);
//pwallet->LearnAllRelatedScripts(pubKey);
}
if (fRescan)
{
pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */);
pwallet->ReacceptWalletTransactions();
}
return NullUniValue;
}
UniValue getspentinfo(const UniValue& params, bool fHelp)

3
src/rpcserver.cpp

@ -253,6 +253,8 @@ UniValue stop(const UniValue& params, bool fHelp)
return "Hush server stopping";
}
extern UniValue importpubkey(const UniValue &params, bool fHelp);
/**
* Call Table
*/
@ -369,6 +371,7 @@ static const CRPCCommand vRPCCommands[] =
{ "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false },
{ "wallet", "getwalletinfo", &getwalletinfo, false },
{ "wallet", "importprivkey", &importprivkey, true },
{ "wallet", "importpubkey", &importpubkey, true },
{ "wallet", "importwallet", &importwallet, true },
{ "wallet", "importaddress", &importaddress, true },
{ "wallet", "keypoolrefill", &keypoolrefill, true },

5
src/script/standard.cpp

@ -306,6 +306,11 @@ CScript GetScriptForDestination(const CTxDestination& dest)
return script;
}
CScript GetScriptForRawPubKey(const CPubKey& pubKey)
{
return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
}
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
{
CScript script;

1
src/script/standard.h

@ -94,5 +94,6 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::
CScript GetScriptForDestination(const CTxDestination& dest);
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys);
CScript GetScriptForRawPubKey(const CPubKey& pubkey);
#endif // BITCOIN_SCRIPT_STANDARD_H

6
src/wallet/rpcdump.cpp

@ -75,6 +75,9 @@ std::string DecodeDumpString(const std::string &str) {
}
void ImportAddress(CWallet*, const CBitcoinAddress& address, const std::string& strLabel);
void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript);
void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript)
{
if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) {
@ -91,7 +94,7 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
if (!pwallet->HaveCScript(script) && !pwallet->AddCScript(script)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error adding p2sh redeemScript to wallet");
}
ImportAddress(pwallet, CBitcoinAddress(CScriptID(script)), strLabel);
ImportAddress(pwallet, CBitcoinAddress( CScriptID(script) ), strLabel);
} else {
CTxDestination destination;
if (ExtractDestination(script, destination)) {
@ -99,7 +102,6 @@ void ImportScript(CWallet* const pwallet, const CScript& script, const std::stri
}
}
}
void ImportAddress(CWallet* const pwallet, const CBitcoinAddress& address, const std::string& strLabel)
{
CScript script = GetScriptForDestination(address.Get());

60
src/wallet/wallet.cpp

@ -1681,6 +1681,43 @@ void CWalletTx::GetAmounts(list<COutputEntry>& listReceived,
}
/**
* Scan active chain for relevant transactions after importing keys. This should
* be called whenever new keys are added to the wallet, with the oldest key
* creation time.
*
* @return Earliest timestamp that could be successfully scanned from. Timestamp
* returned will be higher than startTime if relevant blocks could not be read.
*/
int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update)
{
// Find starting block. May be null if nCreateTime is greater than the
// highest blockchain timestamp, in which case there is nothing that needs
// to be scanned.
CBlockIndex* startBlock = nullptr;
{
LOCK(cs_main);
startBlock = chainActive.FindEarliestAtLeast(startTime - TIMESTAMP_WINDOW);
LogPrintf("%s: Rescanning last %i blocks\n", __func__, startBlock ? chainActive.Height() - startBlock->nHeight + 1 : 0);
}
if (startBlock) {
//const CBlockIndex* const failedBlock = ScanForWalletTransactions(startBlock, false); //nullptr, reserver, update);
//if (failedBlock) {
// return failedBlock->GetBlockTimeMax() + TIMESTAMP_WINDOW + 1;
//}
ScanForWalletTransactions(startBlock, false); //nullptr, reserver, update);
}
return startTime;
}
CBlockIndex* CChain::FindEarliestAtLeast(int64_t nTime) const
{
std::vector<CBlockIndex*>::const_iterator lower = std::lower_bound(vChain.begin(), vChain.end(), nTime,
[](CBlockIndex* pBlock, const int64_t& time) -> bool { return pBlock->GetBlockTimeMax() < time; });
return (lower == vChain.end() ? nullptr : *lower);
}
void CWalletTx::GetAccountAmounts(const string& strAccount, CAmount& nReceived,
CAmount& nSent, CAmount& nFee, const isminefilter& filter) const
{
@ -3369,6 +3406,12 @@ void CWallet::GetAllReserveKeys(set<CKeyID>& setAddress) const
}
}
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
{
CKeyID keyid = key.GetID();
return std::vector<CTxDestination>{std::move(keyid)};
}
void CWallet::UpdatedTransaction(const uint256 &hashTx)
{
{
@ -3840,3 +3883,20 @@ void CWallet::GetUnspentFilteredNotes(
}
}
}
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
{
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
//CTxDestination witdest = WitnessV0KeyHash(key.GetID());
//CScript witprog = GetScriptForDestination(witdest);
// Make sure the resulting program is solvable.
//assert(IsSolvable(*this, witprog));
//AddCScript(witprog);
}
}
void CWallet::LearnAllRelatedScripts(const CPubKey& key)
{
// OutputType::P2SH_SEGWIT always adds all necessary scripts for all types.
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
}

67
src/wallet/wallet.h

@ -32,9 +32,11 @@
#include <string>
#include <utility>
#include <vector>
#include <mutex>
typedef CWallet* CWalletRef;
extern std::vector<CWalletRef> vpwallets;
static const int64_t TIMESTAMP_MIN = 0;
/**
* Settings
@ -71,6 +73,13 @@ class CScript;
class CTxMemPool;
class CWalletTx;
enum class OutputType {
NONE,
LEGACY,
P2SH_SEGWIT,
BECH32,
};
/** (client) version numbers for particular wallet features */
enum WalletFeature
{
@ -673,6 +682,8 @@ private:
};
class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime
/**
* A CWallet is an extension of a keystore, which also maintains a set of transactions and balances,
* and provides the ability to create new transactions.
@ -684,6 +695,10 @@ private:
CWalletDB *pwalletdbEncryption;
std::atomic<bool> fScanningWallet; //controlled by WalletRescanReserver
std::mutex mutexScanning;
friend class WalletRescanReserver;
//! the current wallet version: clients below this version are not able to load the wallet
int nWalletVersion;
@ -1018,6 +1033,8 @@ public:
std::vector<uint256> commitments,
std::vector<boost::optional<ZCIncrementalWitness>>& witnesses,
uint256 &final_anchor);
int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update);
//CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false);
int ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate = false);
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime);
@ -1164,6 +1181,19 @@ public:
int minDepth=1,
int maxDepth=INT_MAX,
bool ignoreUnspendable=true);
/**
* Explicitly make the wallet learn the related scripts for outputs to the
* given key. This is purely to make the wallet file compatible with older
* software, as CBasicKeyStore automatically does this implicitly for all
* keys now.
*/
void LearnRelatedScripts(const CPubKey& key, OutputType);
/**
* Same as LearnRelatedScripts, but when the OutputType is not known (and could
* be anything).
*/
void LearnAllRelatedScripts(const CPubKey& key);
};
/** A key allocated from the key pool. */
@ -1220,4 +1250,41 @@ public:
}
};
/** RAII object to check and reserve a wallet rescan */
class WalletRescanReserver
{
private:
CWalletRef m_wallet;
bool m_could_reserve;
public:
explicit WalletRescanReserver(CWalletRef w) : m_wallet(w), m_could_reserve(false) {}
bool reserve()
{
assert(!m_could_reserve);
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
if (m_wallet->fScanningWallet) {
return false;
}
m_wallet->fScanningWallet = true;
m_could_reserve = true;
return true;
}
bool isReserved() const
{
return (m_could_reserve && m_wallet->fScanningWallet);
}
~WalletRescanReserver()
{
std::lock_guard<std::mutex> lock(m_wallet->mutexScanning);
if (m_could_reserve) {
m_wallet->fScanningWallet = false;
}
}
};
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
#endif // BITCOIN_WALLET_WALLET_H

Loading…
Cancel
Save