aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/scriptpubkeyman.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/scriptpubkeyman.h')
-rw-r--r--src/wallet/scriptpubkeyman.h212
1 files changed, 175 insertions, 37 deletions
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index 8512eadf31..d62d30f339 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -6,6 +6,7 @@
#define BITCOIN_WALLET_SCRIPTPUBKEYMAN_H
#include <psbt.h>
+#include <script/descriptor.h>
#include <script/signingprovider.h>
#include <script/standard.h>
#include <util/error.h>
@@ -17,7 +18,10 @@
#include <boost/signals2/signal.hpp>
+#include <unordered_map>
+
enum class OutputType;
+struct bilingual_str;
// Wallet storage things that ScriptPubKeyMans need in order to be able to store things to the wallet database.
// It provides access to things that are part of the entire wallet and not specific to a ScriptPubKeyMan such as
@@ -108,40 +112,52 @@ public:
CKeyPool();
CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn);
- ADD_SERIALIZE_METHODS;
+ template<typename Stream>
+ void Serialize(Stream& s) const
+ {
+ int nVersion = s.GetVersion();
+ if (!(s.GetType() & SER_GETHASH)) {
+ s << nVersion;
+ }
+ s << nTime << vchPubKey << fInternal << m_pre_split;
+ }
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
+ template<typename Stream>
+ void Unserialize(Stream& s)
+ {
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;
- }
+ if (!(s.GetType() & SER_GETHASH)) {
+ s >> nVersion;
+ }
+ s >> nTime >> vchPubKey;
+ try {
+ s >> 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;
}
- else {
- READWRITE(fInternal);
- READWRITE(m_pre_split);
+ try {
+ s >> 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;
}
}
};
+class KeyIDHasher
+{
+public:
+ KeyIDHasher() {}
+
+ size_t operator()(const CKeyID& id) const
+ {
+ return id.GetUint64(0);
+ }
+};
+
/*
* A class implementing ScriptPubKeyMan manages some (or all) scriptPubKeys used in a wallet.
* It contains the scripts and keys related to the scriptPubKeys it manages.
@@ -190,7 +206,7 @@ public:
virtual bool CanGetAddresses(bool internal = false) const { return false; }
/** Upgrades the wallet to the specified version */
- virtual bool Upgrade(int prev_version, std::string& error) { return false; }
+ virtual bool Upgrade(int prev_version, bilingual_str& error) { return false; }
virtual bool HavePrivateKeys() const { return false; }
@@ -204,7 +220,7 @@ public:
virtual int64_t GetTimeFirstKey() const { return 0; }
- virtual const CKeyMetadata* GetMetadata(const CTxDestination& dest) const { return nullptr; }
+ virtual std::unique_ptr<CKeyMetadata> GetMetadata(const CTxDestination& dest) const { return nullptr; }
virtual std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const { return nullptr; }
@@ -222,6 +238,8 @@ public:
virtual uint256 GetID() const { return uint256(); }
+ virtual void SetInternal(bool internal) {}
+
/** Prepends the wallet name in logging output to ease debugging in multi-wallet use cases */
template<typename... Params>
void WalletLogPrintf(std::string fmt, Params... parameters) const {
@@ -239,7 +257,7 @@ class LegacyScriptPubKeyMan : public ScriptPubKeyMan, public FillableSigningProv
{
private:
//! keeps track of whether Unlock has run a thorough check before
- bool fDecryptionThoroughlyChecked = false;
+ bool fDecryptionThoroughlyChecked = true;
using WatchOnlySet = std::set<CScript>;
using WatchKeyMap = std::map<CKeyID, CPubKey>;
@@ -284,10 +302,11 @@ private:
bool AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info);
/* the HD chain data model (external chain counters) */
- CHDChain hdChain;
+ CHDChain m_hd_chain;
+ std::unordered_map<CKeyID, CHDChain, KeyIDHasher> m_inactive_hd_chains;
/* HD derive new child key (on internal or external chain) */
- void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
+ void DeriveNewChildKey(WalletBatch& batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
std::set<int64_t> setInternalKeyPool GUARDED_BY(cs_KeyStore);
std::set<int64_t> setExternalKeyPool GUARDED_BY(cs_KeyStore);
@@ -316,6 +335,18 @@ private:
*/
bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal);
+ /**
+ * Like TopUp() but adds keys for inactive HD chains.
+ * Ensures that there are at least -keypool number of keys derived after the given index.
+ *
+ * @param seed_id the CKeyID for the HD seed.
+ * @param index the index to start generating keys from
+ * @param internal whether the internal chain should be used. true for internal chain, false for external chain.
+ *
+ * @return true if seed was found and keys were derived. false if unable to derive seeds
+ */
+ bool TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal);
+
public:
using ScriptPubKeyMan::ScriptPubKeyMan;
@@ -340,7 +371,7 @@ public:
bool SetupGeneration(bool force = false) override;
- bool Upgrade(int prev_version, std::string& error) override;
+ bool Upgrade(int prev_version, bilingual_str& error) override;
bool HavePrivateKeys() const override;
@@ -352,7 +383,7 @@ public:
int64_t GetTimeFirstKey() const override;
- const CKeyMetadata* GetMetadata(const CTxDestination& dest) const override;
+ std::unique_ptr<CKeyMetadata> GetMetadata(const CTxDestination& dest) const override;
bool CanGetAddresses(bool internal = false) const override;
@@ -366,6 +397,8 @@ public:
uint256 GetID() const override;
+ void SetInternal(bool internal) override;
+
// Map from Key ID to key metadata.
std::map<CKeyID, CKeyMetadata> mapKeyMetadata GUARDED_BY(cs_KeyStore);
@@ -379,7 +412,7 @@ public:
//! Adds an encrypted key to the store, and saves it to disk.
bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
//! Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
- bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret);
+ bool LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid);
void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
//! Adds a CScript to the store
bool LoadCScript(const CScript& redeemScript);
@@ -387,11 +420,12 @@ public:
void LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata);
void LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata);
//! Generate a new key
- CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
+ CPubKey GenerateNewKey(WalletBatch& batch, CHDChain& hd_chain, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_KeyStore);
/* Set the HD chain model (chain child index counters) */
void SetHDChain(const CHDChain& chain, bool memonly);
- const CHDChain& GetHDChain() const { return hdChain; }
+ const CHDChain& GetHDChain() const { return m_hd_chain; }
+ void AddInactiveHDChain(const CHDChain& chain);
//! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet)
bool LoadWatchOnly(const CScript &dest);
@@ -477,4 +511,108 @@ public:
bool GetKeyOrigin(const CKeyID& keyid, KeyOriginInfo& info) const override { return m_spk_man.GetKeyOrigin(keyid, info); }
};
+class DescriptorScriptPubKeyMan : public ScriptPubKeyMan
+{
+private:
+ WalletDescriptor m_wallet_descriptor GUARDED_BY(cs_desc_man);
+
+ using ScriptPubKeyMap = std::map<CScript, int32_t>; // Map of scripts to descriptor range index
+ using PubKeyMap = std::map<CPubKey, int32_t>; // Map of pubkeys involved in scripts to descriptor range index
+ using CryptedKeyMap = std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char>>>;
+ using KeyMap = std::map<CKeyID, CKey>;
+
+ ScriptPubKeyMap m_map_script_pub_keys GUARDED_BY(cs_desc_man);
+ PubKeyMap m_map_pubkeys GUARDED_BY(cs_desc_man);
+ int32_t m_max_cached_index = -1;
+
+ bool m_internal = false;
+
+ KeyMap m_map_keys GUARDED_BY(cs_desc_man);
+ CryptedKeyMap m_map_crypted_keys GUARDED_BY(cs_desc_man);
+
+ //! keeps track of whether Unlock has run a thorough check before
+ bool m_decryption_thoroughly_checked = false;
+
+ bool AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey);
+
+ KeyMap GetKeys() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
+
+ // Fetch the SigningProvider for the given script and optionally include private keys
+ std::unique_ptr<FlatSigningProvider> GetSigningProvider(const CScript& script, bool include_private = false) const;
+ // Fetch the SigningProvider for the given pubkey and always include private keys. This should only be called by signing code.
+ std::unique_ptr<FlatSigningProvider> GetSigningProvider(const CPubKey& pubkey) const;
+ // Fetch the SigningProvider for a given index and optionally include private keys. Called by the above functions.
+ std::unique_ptr<FlatSigningProvider> GetSigningProvider(int32_t index, bool include_private = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
+
+public:
+ DescriptorScriptPubKeyMan(WalletStorage& storage, WalletDescriptor& descriptor)
+ : ScriptPubKeyMan(storage),
+ m_wallet_descriptor(descriptor)
+ {}
+ DescriptorScriptPubKeyMan(WalletStorage& storage, bool internal)
+ : ScriptPubKeyMan(storage),
+ m_internal(internal)
+ {}
+
+ mutable RecursiveMutex cs_desc_man;
+
+ bool GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) override;
+ isminetype IsMine(const CScript& script) const override;
+
+ bool CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys = false) override;
+ bool Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) override;
+
+ bool GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) override;
+ void ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) override;
+
+ // Tops up the descriptor cache and m_map_script_pub_keys. The cache is stored in the wallet file
+ // and is used to expand the descriptor in GetNewDestination. DescriptorScriptPubKeyMan relies
+ // more on ephemeral data than LegacyScriptPubKeyMan. For wallets using unhardened derivation
+ // (with or without private keys), the "keypool" is a single xpub.
+ bool TopUp(unsigned int size = 0) override;
+
+ void MarkUnusedAddresses(const CScript& script) override;
+
+ bool IsHDEnabled() const override;
+
+ //! Setup descriptors based on the given CExtkey
+ bool SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type);
+
+ bool HavePrivateKeys() const override;
+
+ int64_t GetOldestKeyPoolTime() const override;
+ size_t KeypoolCountExternalKeys() const override;
+ unsigned int GetKeyPoolSize() const override;
+
+ int64_t GetTimeFirstKey() const override;
+
+ std::unique_ptr<CKeyMetadata> GetMetadata(const CTxDestination& dest) const override;
+
+ bool CanGetAddresses(bool internal = false) const override;
+
+ std::unique_ptr<SigningProvider> GetSolvingProvider(const CScript& script) const override;
+
+ bool CanProvide(const CScript& script, SignatureData& sigdata) override;
+
+ bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
+ SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
+
+ uint256 GetID() const override;
+
+ void SetInternal(bool internal) override;
+
+ void SetCache(const DescriptorCache& cache);
+
+ bool AddKey(const CKeyID& key_id, const CKey& key);
+ bool AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key);
+
+ bool HasWalletDescriptor(const WalletDescriptor& desc) const;
+ void AddDescriptorKey(const CKey& key, const CPubKey &pubkey);
+ void WriteDescriptor();
+
+ const WalletDescriptor GetWalletDescriptor() const EXCLUSIVE_LOCKS_REQUIRED(cs_desc_man);
+ const std::vector<CScript> GetScriptPubKeys() const;
+};
+
#endif // BITCOIN_WALLET_SCRIPTPUBKEYMAN_H