MOVEONLY: Move key handling code out of wallet to keyman file
Start moving wallet and ismine code to scriptpubkeyman.h, scriptpubkeyman.cpp The easiest way to review this commit is to run: git log -p -n1 --color-moved=dimmed_zebra And check that everything is a move (other than includes and copyrights comments). This commit is move-only and doesn't change code or affect behavior.
This commit is contained in:
parent
ab053ec6d1
commit
6702048f91
10 changed files with 1392 additions and 1366 deletions
|
@ -235,6 +235,7 @@ BITCOIN_CORE_H = \
|
|||
wallet/load.h \
|
||||
wallet/psbtwallet.h \
|
||||
wallet/rpcwallet.h \
|
||||
wallet/scriptpubkeyman.h \
|
||||
wallet/wallet.h \
|
||||
wallet/walletdb.h \
|
||||
wallet/wallettool.h \
|
||||
|
@ -338,11 +339,11 @@ libbitcoin_wallet_a_SOURCES = \
|
|||
wallet/db.cpp \
|
||||
wallet/feebumper.cpp \
|
||||
wallet/fees.cpp \
|
||||
wallet/ismine.cpp \
|
||||
wallet/load.cpp \
|
||||
wallet/psbtwallet.cpp \
|
||||
wallet/rpcdump.cpp \
|
||||
wallet/rpcwallet.cpp \
|
||||
wallet/scriptpubkeyman.cpp \
|
||||
wallet/wallet.cpp \
|
||||
wallet/walletdb.cpp \
|
||||
wallet/walletutil.cpp \
|
||||
|
|
|
@ -63,8 +63,6 @@ FlatSigningProvider Merge(const FlatSigningProvider& a, const FlatSigningProvide
|
|||
class FillableSigningProvider : public SigningProvider
|
||||
{
|
||||
protected:
|
||||
mutable CCriticalSection cs_KeyStore;
|
||||
|
||||
using KeyMap = std::map<CKeyID, CKey>;
|
||||
using ScriptMap = std::map<CScriptID, CScript>;
|
||||
|
||||
|
@ -74,6 +72,8 @@ protected:
|
|||
void ImplicitlyLearnRelatedKeyScripts(const CPubKey& pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
|
||||
|
||||
public:
|
||||
mutable CCriticalSection cs_KeyStore;
|
||||
|
||||
virtual bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey);
|
||||
virtual bool AddKey(const CKey &key) { return AddKeyPubKey(key, key.GetPubKey()); }
|
||||
virtual bool GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const override;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#define BITCOIN_UTIL_TRANSLATION_H
|
||||
|
||||
#include <tinyformat.h>
|
||||
|
||||
#include <functional>
|
||||
|
||||
/**
|
||||
* Bilingual messages:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <util/moneystr.h>
|
||||
#include <util/system.h>
|
||||
#include <util/translation.h>
|
||||
#include <wallet/scriptpubkeyman.h>
|
||||
#include <wallet/wallet.h>
|
||||
#include <walletinitinterface.h>
|
||||
|
||||
|
|
|
@ -1,192 +0,0 @@
|
|||
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2018 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <wallet/ismine.h>
|
||||
|
||||
#include <key.h>
|
||||
#include <script/script.h>
|
||||
#include <script/signingprovider.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* This is an enum that tracks the execution context of a script, similar to
|
||||
* SigVersion in script/interpreter. It is separate however because we want to
|
||||
* distinguish between top-level scriptPubKey execution and P2SH redeemScript
|
||||
* execution (a distinction that has no impact on consensus rules).
|
||||
*/
|
||||
enum class IsMineSigVersion
|
||||
{
|
||||
TOP = 0, //!< scriptPubKey execution
|
||||
P2SH = 1, //!< P2SH redeemScript
|
||||
WITNESS_V0 = 2, //!< P2WSH witness script execution
|
||||
};
|
||||
|
||||
/**
|
||||
* This is an internal representation of isminetype + invalidity.
|
||||
* Its order is significant, as we return the max of all explored
|
||||
* possibilities.
|
||||
*/
|
||||
enum class IsMineResult
|
||||
{
|
||||
NO = 0, //!< Not ours
|
||||
WATCH_ONLY = 1, //!< Included in watch-only balance
|
||||
SPENDABLE = 2, //!< Included in all balances
|
||||
INVALID = 3, //!< Not spendable by anyone (uncompressed pubkey in segwit, P2SH inside P2SH or witness, witness inside witness)
|
||||
};
|
||||
|
||||
bool PermitsUncompressed(IsMineSigVersion sigversion)
|
||||
{
|
||||
return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH;
|
||||
}
|
||||
|
||||
bool HaveKeys(const std::vector<valtype>& pubkeys, const CWallet& keystore)
|
||||
{
|
||||
for (const valtype& pubkey : pubkeys) {
|
||||
CKeyID keyID = CPubKey(pubkey).GetID();
|
||||
if (!keystore.HaveKey(keyID)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
IsMineResult IsMineInner(const CWallet& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion)
|
||||
{
|
||||
IsMineResult ret = IsMineResult::NO;
|
||||
|
||||
std::vector<valtype> vSolutions;
|
||||
txnouttype whichType = Solver(scriptPubKey, vSolutions);
|
||||
|
||||
CKeyID keyID;
|
||||
switch (whichType)
|
||||
{
|
||||
case TX_NONSTANDARD:
|
||||
case TX_NULL_DATA:
|
||||
case TX_WITNESS_UNKNOWN:
|
||||
break;
|
||||
case TX_PUBKEY:
|
||||
keyID = CPubKey(vSolutions[0]).GetID();
|
||||
if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
|
||||
return IsMineResult::INVALID;
|
||||
}
|
||||
if (keystore.HaveKey(keyID)) {
|
||||
ret = std::max(ret, IsMineResult::SPENDABLE);
|
||||
}
|
||||
break;
|
||||
case TX_WITNESS_V0_KEYHASH:
|
||||
{
|
||||
if (sigversion == IsMineSigVersion::WITNESS_V0) {
|
||||
// P2WPKH inside P2WSH is invalid.
|
||||
return IsMineResult::INVALID;
|
||||
}
|
||||
if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
|
||||
// We do not support bare witness outputs unless the P2SH version of it would be
|
||||
// acceptable as well. This protects against matching before segwit activates.
|
||||
// This also applies to the P2WSH case.
|
||||
break;
|
||||
}
|
||||
ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
|
||||
break;
|
||||
}
|
||||
case TX_PUBKEYHASH:
|
||||
keyID = CKeyID(uint160(vSolutions[0]));
|
||||
if (!PermitsUncompressed(sigversion)) {
|
||||
CPubKey pubkey;
|
||||
if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) {
|
||||
return IsMineResult::INVALID;
|
||||
}
|
||||
}
|
||||
if (keystore.HaveKey(keyID)) {
|
||||
ret = std::max(ret, IsMineResult::SPENDABLE);
|
||||
}
|
||||
break;
|
||||
case TX_SCRIPTHASH:
|
||||
{
|
||||
if (sigversion != IsMineSigVersion::TOP) {
|
||||
// P2SH inside P2WSH or P2SH is invalid.
|
||||
return IsMineResult::INVALID;
|
||||
}
|
||||
CScriptID scriptID = CScriptID(uint160(vSolutions[0]));
|
||||
CScript subscript;
|
||||
if (keystore.GetCScript(scriptID, subscript)) {
|
||||
ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TX_WITNESS_V0_SCRIPTHASH:
|
||||
{
|
||||
if (sigversion == IsMineSigVersion::WITNESS_V0) {
|
||||
// P2WSH inside P2WSH is invalid.
|
||||
return IsMineResult::INVALID;
|
||||
}
|
||||
if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) {
|
||||
break;
|
||||
}
|
||||
uint160 hash;
|
||||
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(hash.begin());
|
||||
CScriptID scriptID = CScriptID(hash);
|
||||
CScript subscript;
|
||||
if (keystore.GetCScript(scriptID, subscript)) {
|
||||
ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TX_MULTISIG:
|
||||
{
|
||||
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
|
||||
if (sigversion == IsMineSigVersion::TOP) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Only consider transactions "mine" if we own ALL the
|
||||
// keys involved. Multi-signature transactions that are
|
||||
// partially owned (somebody else has a key that can spend
|
||||
// them) enable spend-out-from-under-you attacks, especially
|
||||
// in shared-wallet situations.
|
||||
std::vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin()+vSolutions.size()-1);
|
||||
if (!PermitsUncompressed(sigversion)) {
|
||||
for (size_t i = 0; i < keys.size(); i++) {
|
||||
if (keys[i].size() != 33) {
|
||||
return IsMineResult::INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (HaveKeys(keys, keystore)) {
|
||||
ret = std::max(ret, IsMineResult::SPENDABLE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {
|
||||
ret = std::max(ret, IsMineResult::WATCH_ONLY);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
isminetype IsMine(const CWallet& keystore, const CScript& scriptPubKey)
|
||||
{
|
||||
switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) {
|
||||
case IsMineResult::INVALID:
|
||||
case IsMineResult::NO:
|
||||
return ISMINE_NO;
|
||||
case IsMineResult::WATCH_ONLY:
|
||||
return ISMINE_WATCH_ONLY;
|
||||
case IsMineResult::SPENDABLE:
|
||||
return ISMINE_SPENDABLE;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
isminetype IsMine(const CWallet& keystore, const CTxDestination& dest)
|
||||
{
|
||||
CScript script = GetScriptForDestination(dest);
|
||||
return IsMine(keystore, script);
|
||||
}
|
1262
src/wallet/scriptpubkeyman.cpp
Normal file
1262
src/wallet/scriptpubkeyman.cpp
Normal file
File diff suppressed because it is too large
Load diff
115
src/wallet/scriptpubkeyman.h
Normal file
115
src/wallet/scriptpubkeyman.h
Normal file
|
@ -0,0 +1,115 @@
|
|||
// Copyright (c) 2019 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
|
||||
#define BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
|
||||
|
||||
#include <script/signingprovider.h>
|
||||
#include <script/standard.h>
|
||||
#include <wallet/crypter.h>
|
||||
#include <wallet/ismine.h>
|
||||
#include <wallet/walletdb.h>
|
||||
#include <wallet/walletutil.h>
|
||||
|
||||
#include <boost/signals2/signal.hpp>
|
||||
|
||||
enum class OutputType;
|
||||
|
||||
//! Default for -keypool
|
||||
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
|
||||
|
||||
/** A key from a CWallet's keypool
|
||||
*
|
||||
* The wallet holds one (for pre HD-split wallets) or several keypools. These
|
||||
* are sets of keys that have not yet been used to provide addresses or receive
|
||||
* change.
|
||||
*
|
||||
* The Bitcoin Core wallet was originally a collection of unrelated private
|
||||
* keys with their associated addresses. If a non-HD wallet generated a
|
||||
* key/address, gave that address out and then restored a backup from before
|
||||
* that key's generation, then any funds sent to that address would be
|
||||
* lost definitively.
|
||||
*
|
||||
* The keypool was implemented to avoid this scenario (commit: 10384941). The
|
||||
* wallet would generate a set of keys (100 by default). When a new public key
|
||||
* was required, either to give out as an address or to use in a change output,
|
||||
* it would be drawn from the keypool. The keypool would then be topped up to
|
||||
* maintain 100 keys. This ensured that as long as the wallet hadn't used more
|
||||
* than 100 keys since the previous backup, all funds would be safe, since a
|
||||
* restored wallet would be able to scan for all owned addresses.
|
||||
*
|
||||
* A keypool also allowed encrypted wallets to give out addresses without
|
||||
* having to be decrypted to generate a new private key.
|
||||
*
|
||||
* With the introduction of HD wallets (commit: f1902510), the keypool
|
||||
* essentially became an address look-ahead pool. Restoring old backups can no
|
||||
* longer definitively lose funds as long as the addresses used were from the
|
||||
* wallet's HD seed (since all private keys can be rederived from the seed).
|
||||
* However, if many addresses were used since the backup, then the wallet may
|
||||
* not know how far ahead in the HD chain to look for its addresses. The
|
||||
* keypool is used to implement a 'gap limit'. The keypool maintains a set of
|
||||
* keys (by default 1000) ahead of the last used key and scans for the
|
||||
* addresses of those keys. This avoids the risk of not seeing transactions
|
||||
* involving the wallet's addresses, or of re-using the same address.
|
||||
*
|
||||
* The HD-split wallet feature added a second keypool (commit: 02592f4c). There
|
||||
* is an external keypool (for addresses to hand out) and an internal keypool
|
||||
* (for change addresses).
|
||||
*
|
||||
* Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
|
||||
* stored as sets of indexes in the wallet (setInternalKeyPool,
|
||||
* setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
|
||||
* index (m_pool_key_to_index). The CKeyPool object is used to
|
||||
* serialize/deserialize the pool data to/from the database.
|
||||
*/
|
||||
class CKeyPool
|
||||
{
|
||||
public:
|
||||
//! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
|
||||
int64_t nTime;
|
||||
//! The public key
|
||||
CPubKey vchPubKey;
|
||||
//! Whether this keypool entry is in the internal keypool (for change outputs)
|
||||
bool fInternal;
|
||||
//! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
|
||||
bool m_pre_split;
|
||||
|
||||
CKeyPool();
|
||||
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
int nVersion = s.GetVersion();
|
||||
if (!(s.GetType() & SER_GETHASH))
|
||||
READWRITE(nVersion);
|
||||
READWRITE(nTime);
|
||||
READWRITE(vchPubKey);
|
||||
if (ser_action.ForRead()) {
|
||||
try {
|
||||
READWRITE(fInternal);
|
||||
}
|
||||
catch (std::ios_base::failure&) {
|
||||
/* flag as external address if we can't read the internal boolean
|
||||
(this will be the case for any wallet before the HD chain split version) */
|
||||
fInternal = false;
|
||||
}
|
||||
try {
|
||||
READWRITE(m_pre_split);
|
||||
}
|
||||
catch (std::ios_base::failure&) {
|
||||
/* flag as postsplit address if we can't read the m_pre_split boolean
|
||||
(this will be the case for any wallet that upgrades to HD chain split)*/
|
||||
m_pre_split = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
READWRITE(fInternal);
|
||||
READWRITE(m_pre_split);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
|
File diff suppressed because it is too large
Load diff
|
@ -57,8 +57,6 @@ enum class WalletCreationStatus {
|
|||
|
||||
WalletCreationStatus CreateWallet(interfaces::Chain& chain, const SecureString& passphrase, uint64_t wallet_creation_flags, const std::string& name, std::string& error, std::vector<std::string>& warnings, std::shared_ptr<CWallet>& result);
|
||||
|
||||
//! Default for -keypool
|
||||
static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
|
||||
//! -paytxfee default
|
||||
constexpr CAmount DEFAULT_PAY_TX_FEE = 0;
|
||||
//! -fallbackfee default
|
||||
|
@ -123,99 +121,6 @@ static const std::map<std::string,WalletFlags> WALLET_FLAG_MAP{
|
|||
|
||||
extern const std::map<uint64_t,std::string> WALLET_FLAG_CAVEATS;
|
||||
|
||||
/** A key from a CWallet's keypool
|
||||
*
|
||||
* The wallet holds one (for pre HD-split wallets) or several keypools. These
|
||||
* are sets of keys that have not yet been used to provide addresses or receive
|
||||
* change.
|
||||
*
|
||||
* The Bitcoin Core wallet was originally a collection of unrelated private
|
||||
* keys with their associated addresses. If a non-HD wallet generated a
|
||||
* key/address, gave that address out and then restored a backup from before
|
||||
* that key's generation, then any funds sent to that address would be
|
||||
* lost definitively.
|
||||
*
|
||||
* The keypool was implemented to avoid this scenario (commit: 10384941). The
|
||||
* wallet would generate a set of keys (100 by default). When a new public key
|
||||
* was required, either to give out as an address or to use in a change output,
|
||||
* it would be drawn from the keypool. The keypool would then be topped up to
|
||||
* maintain 100 keys. This ensured that as long as the wallet hadn't used more
|
||||
* than 100 keys since the previous backup, all funds would be safe, since a
|
||||
* restored wallet would be able to scan for all owned addresses.
|
||||
*
|
||||
* A keypool also allowed encrypted wallets to give out addresses without
|
||||
* having to be decrypted to generate a new private key.
|
||||
*
|
||||
* With the introduction of HD wallets (commit: f1902510), the keypool
|
||||
* essentially became an address look-ahead pool. Restoring old backups can no
|
||||
* longer definitively lose funds as long as the addresses used were from the
|
||||
* wallet's HD seed (since all private keys can be rederived from the seed).
|
||||
* However, if many addresses were used since the backup, then the wallet may
|
||||
* not know how far ahead in the HD chain to look for its addresses. The
|
||||
* keypool is used to implement a 'gap limit'. The keypool maintains a set of
|
||||
* keys (by default 1000) ahead of the last used key and scans for the
|
||||
* addresses of those keys. This avoids the risk of not seeing transactions
|
||||
* involving the wallet's addresses, or of re-using the same address.
|
||||
*
|
||||
* The HD-split wallet feature added a second keypool (commit: 02592f4c). There
|
||||
* is an external keypool (for addresses to hand out) and an internal keypool
|
||||
* (for change addresses).
|
||||
*
|
||||
* Keypool keys are stored in the wallet/keystore's keymap. The keypool data is
|
||||
* stored as sets of indexes in the wallet (setInternalKeyPool,
|
||||
* setExternalKeyPool and set_pre_split_keypool), and a map from the key to the
|
||||
* index (m_pool_key_to_index). The CKeyPool object is used to
|
||||
* serialize/deserialize the pool data to/from the database.
|
||||
*/
|
||||
class CKeyPool
|
||||
{
|
||||
public:
|
||||
//! The time at which the key was generated. Set in AddKeypoolPubKeyWithDB
|
||||
int64_t nTime;
|
||||
//! The public key
|
||||
CPubKey vchPubKey;
|
||||
//! Whether this keypool entry is in the internal keypool (for change outputs)
|
||||
bool fInternal;
|
||||
//! Whether this key was generated for a keypool before the wallet was upgraded to HD-split
|
||||
bool m_pre_split;
|
||||
|
||||
CKeyPool();
|
||||
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
int nVersion = s.GetVersion();
|
||||
if (!(s.GetType() & SER_GETHASH))
|
||||
READWRITE(nVersion);
|
||||
READWRITE(nTime);
|
||||
READWRITE(vchPubKey);
|
||||
if (ser_action.ForRead()) {
|
||||
try {
|
||||
READWRITE(fInternal);
|
||||
}
|
||||
catch (std::ios_base::failure&) {
|
||||
/* flag as external address if we can't read the internal boolean
|
||||
(this will be the case for any wallet before the HD chain split version) */
|
||||
fInternal = false;
|
||||
}
|
||||
try {
|
||||
READWRITE(m_pre_split);
|
||||
}
|
||||
catch (std::ios_base::failure&) {
|
||||
/* flag as postsplit address if we can't read the m_pre_split boolean
|
||||
(this will be the case for any wallet that upgrades to HD chain split)*/
|
||||
m_pre_split = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
READWRITE(fInternal);
|
||||
READWRITE(m_pre_split);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** A wrapper to reserve an address from a wallet
|
||||
*
|
||||
* ReserveDestination is used to reserve an address.
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <sync.h>
|
||||
#include <util/system.h>
|
||||
#include <util/time.h>
|
||||
#include <wallet/scriptpubkeyman.h>
|
||||
#include <wallet/wallet.h>
|
||||
|
||||
#include <atomic>
|
||||
|
|
Loading…
Reference in a new issue