// Copyright (c) 2019-2020 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 <key_io.h> #include <logging.h> #include <outputtype.h> #include <script/descriptor.h> #include <script/sign.h> #include <util/bip32.h> #include <util/strencodings.h> #include <util/string.h> #include <util/system.h> #include <util/time.h> #include <util/translation.h> #include <wallet/external_signer.h> #include <wallet/scriptpubkeyman.h> //! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details. const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000; bool LegacyScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { LOCK(cs_KeyStore); error.clear(); // Generate a new key that is added to wallet CPubKey new_key; if (!GetKeyFromPool(new_key, type)) { error = _("Error: Keypool ran out, please call keypoolrefill first").translated; return false; } LearnRelatedScripts(new_key, type); dest = GetDestinationForKey(new_key, type); return true; } 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 LegacyScriptPubKeyMan& keystore) { for (const valtype& pubkey : pubkeys) { CKeyID keyID = CPubKey(pubkey).GetID(); if (!keystore.HaveKey(keyID)) return false; } return true; } //! Recursively solve script and return spendable/watchonly/invalid status. //! //! @param keystore legacy key and script store //! @param script script to solve //! @param sigversion script type (top-level / redeemscript / witnessscript) //! @param recurse_scripthash whether to recurse into nested p2sh and p2wsh //! scripts or simply treat any script that has been //! stored in the keystore as spendable IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion, bool recurse_scripthash=true) { IsMineResult ret = IsMineResult::NO; std::vector<valtype> vSolutions; TxoutType whichType = Solver(scriptPubKey, vSolutions); CKeyID keyID; switch (whichType) { case TxoutType::NONSTANDARD: case TxoutType::NULL_DATA: case TxoutType::WITNESS_UNKNOWN: case TxoutType::WITNESS_V1_TAPROOT: break; case TxoutType::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 TxoutType::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 TxoutType::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 TxoutType::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, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::P2SH) : IsMineResult::SPENDABLE); } break; } case TxoutType::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, recurse_scripthash ? IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0) : IsMineResult::SPENDABLE); } break; } case TxoutType::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; } } // no default case, so the compiler can warn about missing cases if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) { ret = std::max(ret, IsMineResult::WATCH_ONLY); } return ret; } } // namespace isminetype LegacyScriptPubKeyMan::IsMine(const CScript& script) const { switch (IsMineInner(*this, script, 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); } bool LegacyScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys) { { LOCK(cs_KeyStore); assert(mapKeys.empty()); bool keyPass = mapCryptedKeys.empty(); // Always pass when there are no encrypted keys bool keyFail = false; CryptedKeyMap::const_iterator mi = mapCryptedKeys.begin(); WalletBatch batch(m_storage.GetDatabase()); for (; mi != mapCryptedKeys.end(); ++mi) { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; CKey key; if (!DecryptKey(master_key, vchCryptedSecret, vchPubKey, key)) { keyFail = true; break; } keyPass = true; if (fDecryptionThoroughlyChecked) break; else { // Rewrite these encrypted keys with checksums batch.WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } } if (keyPass && keyFail) { LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt."); } if (keyFail || (!keyPass && !accept_no_keys)) return false; fDecryptionThoroughlyChecked = true; } return true; } bool LegacyScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { LOCK(cs_KeyStore); encrypted_batch = batch; if (!mapCryptedKeys.empty()) { encrypted_batch = nullptr; return false; } KeyMap keys_to_encrypt; keys_to_encrypt.swap(mapKeys); // Clear mapKeys so AddCryptedKeyInner will succeed. for (const KeyMap::value_type& mKey : keys_to_encrypt) { const CKey &key = mKey.second; CPubKey vchPubKey = key.GetPubKey(); CKeyingMaterial vchSecret(key.begin(), key.end()); std::vector<unsigned char> vchCryptedSecret; if (!EncryptSecret(master_key, vchSecret, vchPubKey.GetHash(), vchCryptedSecret)) { encrypted_batch = nullptr; return false; } if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) { encrypted_batch = nullptr; return false; } } encrypted_batch = nullptr; return true; } bool LegacyScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { LOCK(cs_KeyStore); if (!CanGetAddresses(internal)) { return false; } if (!ReserveKeyFromKeyPool(index, keypool, internal)) { return false; } address = GetDestinationForKey(keypool.vchPubKey, type); return true; } bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, bool internal) { LOCK(cs_KeyStore); if (m_storage.IsLocked()) return false; auto it = m_inactive_hd_chains.find(seed_id); if (it == m_inactive_hd_chains.end()) { return false; } CHDChain& chain = it->second; // Top up key pool int64_t target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1); // "size" of the keypools. Not really the size, actually the difference between index and the chain counter // Since chain counter is 1 based and index is 0 based, one of them needs to be offset by 1. int64_t kp_size = (internal ? chain.nInternalChainCounter : chain.nExternalChainCounter) - (index + 1); // make sure the keypool fits the user-selected target (-keypool) int64_t missing = std::max(target_size - kp_size, (int64_t) 0); if (missing > 0) { WalletBatch batch(m_storage.GetDatabase()); for (int64_t i = missing; i > 0; --i) { GenerateNewKey(batch, chain, internal); } if (internal) { WalletLogPrintf("inactive seed with id %s added %d internal keys\n", HexStr(seed_id), missing); } else { WalletLogPrintf("inactive seed with id %s added %d keys\n", HexStr(seed_id), missing); } } return true; } void LegacyScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) { LOCK(cs_KeyStore); // extract addresses and check if they match with an unused keypool key for (const auto& keyid : GetAffectedKeys(script, *this)) { std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid); if (mi != m_pool_key_to_index.end()) { WalletLogPrintf("%s: Detected a used keypool key, mark all keypool keys up to this key as used\n", __func__); MarkReserveKeysAsUsed(mi->second); if (!TopUp()) { WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); } } // Find the key's metadata and check if it's seed id (if it has one) is inactive, i.e. it is not the current m_hd_chain seed id. // If so, TopUp the inactive hd chain auto it = mapKeyMetadata.find(keyid); if (it != mapKeyMetadata.end()){ CKeyMetadata meta = it->second; if (!meta.hd_seed_id.IsNull() && meta.hd_seed_id != m_hd_chain.seed_id) { bool internal = (meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0; int64_t index = meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT; if (!TopUpInactiveHDChain(meta.hd_seed_id, index, internal)) { WalletLogPrintf("%s: Adding inactive seed keys failed\n", __func__); } } } } } void LegacyScriptPubKeyMan::UpgradeKeyMetadata() { LOCK(cs_KeyStore); if (m_storage.IsLocked() || m_storage.IsWalletFlagSet(WALLET_FLAG_KEY_ORIGIN_METADATA)) { return; } std::unique_ptr<WalletBatch> batch = MakeUnique<WalletBatch>(m_storage.GetDatabase()); for (auto& meta_pair : mapKeyMetadata) { CKeyMetadata& meta = meta_pair.second; if (!meta.hd_seed_id.IsNull() && !meta.has_key_origin && meta.hdKeypath != "s") { // If the hdKeypath is "s", that's the seed and it doesn't have a key origin CKey key; GetKey(meta.hd_seed_id, key); CExtKey masterKey; masterKey.SetSeed(key.begin(), key.size()); // Add to map CKeyID master_id = masterKey.key.GetPubKey().GetID(); std::copy(master_id.begin(), master_id.begin() + 4, meta.key_origin.fingerprint); if (!ParseHDKeypath(meta.hdKeypath, meta.key_origin.path)) { throw std::runtime_error("Invalid stored hdKeypath"); } meta.has_key_origin = true; if (meta.nVersion < CKeyMetadata::VERSION_WITH_KEY_ORIGIN) { meta.nVersion = CKeyMetadata::VERSION_WITH_KEY_ORIGIN; } // Write meta to wallet CPubKey pubkey; if (GetPubKey(meta_pair.first, pubkey)) { batch->WriteKeyMetadata(meta, pubkey, true); } } } } bool LegacyScriptPubKeyMan::SetupGeneration(bool force) { if ((CanGenerateKeys() && !force) || m_storage.IsLocked()) { return false; } SetHDSeed(GenerateNewSeed()); if (!NewKeyPool()) { return false; } return true; } bool LegacyScriptPubKeyMan::IsHDEnabled() const { return !m_hd_chain.seed_id.IsNull(); } bool LegacyScriptPubKeyMan::CanGetAddresses(bool internal) const { LOCK(cs_KeyStore); // Check if the keypool has keys bool keypool_has_keys; if (internal && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { keypool_has_keys = setInternalKeyPool.size() > 0; } else { keypool_has_keys = KeypoolCountExternalKeys() > 0; } // If the keypool doesn't have keys, check if we can generate them if (!keypool_has_keys) { return CanGenerateKeys(); } return keypool_has_keys; } bool LegacyScriptPubKeyMan::Upgrade(int prev_version, int new_version, bilingual_str& error) { LOCK(cs_KeyStore); bool hd_upgrade = false; bool split_upgrade = false; if (IsFeatureSupported(new_version, FEATURE_HD) && !IsHDEnabled()) { WalletLogPrintf("Upgrading wallet to HD\n"); m_storage.SetMinVersion(FEATURE_HD); // generate a new master key CPubKey masterPubKey = GenerateNewSeed(); SetHDSeed(masterPubKey); hd_upgrade = true; } // Upgrade to HD chain split if necessary if (!IsFeatureSupported(prev_version, FEATURE_HD_SPLIT) && IsFeatureSupported(new_version, FEATURE_HD_SPLIT)) { WalletLogPrintf("Upgrading wallet to use HD chain split\n"); m_storage.SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); split_upgrade = FEATURE_HD_SPLIT > prev_version; // Upgrade the HDChain if (m_hd_chain.nVersion < CHDChain::VERSION_HD_CHAIN_SPLIT) { m_hd_chain.nVersion = CHDChain::VERSION_HD_CHAIN_SPLIT; if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(m_hd_chain)) { throw std::runtime_error(std::string(__func__) + ": writing chain failed"); } } } // Mark all keys currently in the keypool as pre-split if (split_upgrade) { MarkPreSplitKeys(); } // Regenerate the keypool if upgraded to HD if (hd_upgrade) { if (!TopUp()) { error = _("Unable to generate keys"); return false; } } return true; } bool LegacyScriptPubKeyMan::HavePrivateKeys() const { LOCK(cs_KeyStore); return !mapKeys.empty() || !mapCryptedKeys.empty(); } void LegacyScriptPubKeyMan::RewriteDB() { LOCK(cs_KeyStore); setInternalKeyPool.clear(); setExternalKeyPool.clear(); m_pool_key_to_index.clear(); // Note: can't top-up keypool here, because wallet is locked. // User will be prompted to unlock wallet the next operation // that requires a new key. } static int64_t GetOldestKeyTimeInPool(const std::set<int64_t>& setKeyPool, WalletBatch& batch) { if (setKeyPool.empty()) { return GetTime(); } CKeyPool keypool; int64_t nIndex = *(setKeyPool.begin()); if (!batch.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed"); } assert(keypool.vchPubKey.IsValid()); return keypool.nTime; } int64_t LegacyScriptPubKeyMan::GetOldestKeyPoolTime() const { LOCK(cs_KeyStore); WalletBatch batch(m_storage.GetDatabase()); // load oldest key from keypool, get time and return int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); if (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); if (!set_pre_split_keypool.empty()) { oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey); } } return oldestKey; } size_t LegacyScriptPubKeyMan::KeypoolCountExternalKeys() const { LOCK(cs_KeyStore); return setExternalKeyPool.size() + set_pre_split_keypool.size(); } unsigned int LegacyScriptPubKeyMan::GetKeyPoolSize() const { LOCK(cs_KeyStore); return setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(); } int64_t LegacyScriptPubKeyMan::GetTimeFirstKey() const { LOCK(cs_KeyStore); return nTimeFirstKey; } std::unique_ptr<SigningProvider> LegacyScriptPubKeyMan::GetSolvingProvider(const CScript& script) const { return MakeUnique<LegacySigningProvider>(*this); } bool LegacyScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata) { IsMineResult ismine = IsMineInner(*this, script, IsMineSigVersion::TOP, /* recurse_scripthash= */ false); if (ismine == IsMineResult::SPENDABLE || ismine == IsMineResult::WATCH_ONLY) { // If ismine, it means we recognize keys or script ids in the script, or // are watching the script itself, and we can at least provide metadata // or solving information, even if not able to sign fully. return true; } else { // If, given the stuff in sigdata, we could make a valid sigature, then we can provide for this script ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, script, sigdata); if (!sigdata.signatures.empty()) { // If we could make signatures, make sure we have a private key to actually make a signature bool has_privkeys = false; for (const auto& key_sig_pair : sigdata.signatures) { has_privkeys |= HaveKey(key_sig_pair.first); } return has_privkeys; } return false; } } bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const { return ::SignTransaction(tx, this, coins, sighash, input_errors); } SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { CKey key; if (!GetKey(ToKeyID(pkhash), key)) { return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; } if (MessageSign(key, message, str_sig)) { return SigningResult::OK; } return SigningResult::SIGNING_FAILED; } TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const { if (n_signed) { *n_signed = 0; } for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { const CTxIn& txin = psbtx.tx->vin[i]; PSBTInput& input = psbtx.inputs.at(i); if (PSBTInputSigned(input)) { continue; } // Get the Sighash type if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) { return TransactionError::SIGHASH_MISMATCH; } // Check non_witness_utxo has specified prevout if (input.non_witness_utxo) { if (txin.prevout.n >= input.non_witness_utxo->vout.size()) { return TransactionError::MISSING_INPUTS; } } else if (input.witness_utxo.IsNull()) { // There's no UTXO so we can just skip this now continue; } SignatureData sigdata; input.FillSignatureData(sigdata); SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type); bool signed_one = PSBTInputSigned(input); if (n_signed && (signed_one || !sign)) { // If sign is false, we assume that we _could_ sign if we get here. This // will never have false negatives; it is hard to tell under what i // circumstances it could have false positives. (*n_signed)++; } } // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { UpdatePSBTOutput(HidingSigningProvider(this, true, !bip32derivs), psbtx, i); } return TransactionError::OK; } std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const { LOCK(cs_KeyStore); CKeyID key_id = GetKeyForDestination(*this, dest); if (!key_id.IsNull()) { auto it = mapKeyMetadata.find(key_id); if (it != mapKeyMetadata.end()) { return MakeUnique<CKeyMetadata>(it->second); } } CScript scriptPubKey = GetScriptForDestination(dest); auto it = m_script_metadata.find(CScriptID(scriptPubKey)); if (it != m_script_metadata.end()) { return MakeUnique<CKeyMetadata>(it->second); } return nullptr; } uint256 LegacyScriptPubKeyMan::GetID() const { return uint256::ONE; } /** * Update wallet first key creation time. This should be called whenever keys * are added to the wallet, with the oldest key creation time. */ void LegacyScriptPubKeyMan::UpdateTimeFirstKey(int64_t nCreateTime) { AssertLockHeld(cs_KeyStore); if (nCreateTime <= 1) { // Cannot determine birthday information, so set the wallet birthday to // the beginning of time. nTimeFirstKey = 1; } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) { nTimeFirstKey = nCreateTime; } } bool LegacyScriptPubKeyMan::LoadKey(const CKey& key, const CPubKey &pubkey) { return AddKeyPubKeyInner(key, pubkey); } bool LegacyScriptPubKeyMan::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { LOCK(cs_KeyStore); WalletBatch batch(m_storage.GetDatabase()); return LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(batch, secret, pubkey); } bool LegacyScriptPubKeyMan::AddKeyPubKeyWithDB(WalletBatch& batch, const CKey& secret, const CPubKey& pubkey) { AssertLockHeld(cs_KeyStore); // Make sure we aren't adding private keys to private key disabled wallets assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); // FillableSigningProvider has no concept of wallet databases, but calls AddCryptedKey // which is overridden below. To avoid flushes, the database handle is // tunneled through to it. bool needsDB = !encrypted_batch; if (needsDB) { encrypted_batch = &batch; } if (!AddKeyPubKeyInner(secret, pubkey)) { if (needsDB) encrypted_batch = nullptr; return false; } if (needsDB) encrypted_batch = nullptr; // check if we need to remove from watch-only CScript script; script = GetScriptForDestination(PKHash(pubkey)); if (HaveWatchOnly(script)) { RemoveWatchOnly(script); } script = GetScriptForRawPubKey(pubkey); if (HaveWatchOnly(script)) { RemoveWatchOnly(script); } if (!m_storage.HasEncryptionKeys()) { return batch.WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } m_storage.UnsetBlankWalletFlag(batch); return true; } bool LegacyScriptPubKeyMan::LoadCScript(const CScript& redeemScript) { /* A sanity check was added in pull #3843 to avoid adding redeemScripts * that never can be redeemed. However, old wallets may still contain * these. Do not add them to the wallet and warn. */ if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE) { std::string strAddr = EncodeDestination(ScriptHash(redeemScript)); WalletLogPrintf("%s: Warning: This wallet contains a redeemScript of size %i which exceeds maximum size %i thus can never be redeemed. Do not use address %s.\n", __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr); return true; } return FillableSigningProvider::AddCScript(redeemScript); } void LegacyScriptPubKeyMan::LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata& meta) { LOCK(cs_KeyStore); UpdateTimeFirstKey(meta.nCreateTime); mapKeyMetadata[keyID] = meta; } void LegacyScriptPubKeyMan::LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata& meta) { LOCK(cs_KeyStore); UpdateTimeFirstKey(meta.nCreateTime); m_script_metadata[script_id] = meta; } bool LegacyScriptPubKeyMan::AddKeyPubKeyInner(const CKey& key, const CPubKey &pubkey) { LOCK(cs_KeyStore); if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::AddKeyPubKey(key, pubkey); } if (m_storage.IsLocked()) { return false; } std::vector<unsigned char> vchCryptedSecret; CKeyingMaterial vchSecret(key.begin(), key.end()); if (!EncryptSecret(m_storage.GetEncryptionKey(), vchSecret, pubkey.GetHash(), vchCryptedSecret)) { return false; } if (!AddCryptedKey(pubkey, vchCryptedSecret)) { return false; } return true; } bool LegacyScriptPubKeyMan::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret, bool checksum_valid) { // Set fDecryptionThoroughlyChecked to false when the checksum is invalid if (!checksum_valid) { fDecryptionThoroughlyChecked = false; } return AddCryptedKeyInner(vchPubKey, vchCryptedSecret); } bool LegacyScriptPubKeyMan::AddCryptedKeyInner(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { LOCK(cs_KeyStore); assert(mapKeys.empty()); mapCryptedKeys[vchPubKey.GetID()] = make_pair(vchPubKey, vchCryptedSecret); ImplicitlyLearnRelatedKeyScripts(vchPubKey); return true; } bool LegacyScriptPubKeyMan::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { if (!AddCryptedKeyInner(vchPubKey, vchCryptedSecret)) return false; { LOCK(cs_KeyStore); if (encrypted_batch) return encrypted_batch->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); else return WalletBatch(m_storage.GetDatabase()).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } } bool LegacyScriptPubKeyMan::HaveWatchOnly(const CScript &dest) const { LOCK(cs_KeyStore); return setWatchOnly.count(dest) > 0; } bool LegacyScriptPubKeyMan::HaveWatchOnly() const { LOCK(cs_KeyStore); return (!setWatchOnly.empty()); } static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut) { std::vector<std::vector<unsigned char>> solutions; return Solver(dest, solutions) == TxoutType::PUBKEY && (pubKeyOut = CPubKey(solutions[0])).IsFullyValid(); } bool LegacyScriptPubKeyMan::RemoveWatchOnly(const CScript &dest) { { LOCK(cs_KeyStore); setWatchOnly.erase(dest); CPubKey pubKey; if (ExtractPubKey(dest, pubKey)) { mapWatchKeys.erase(pubKey.GetID()); } // Related CScripts are not removed; having superfluous scripts around is // harmless (see comment in ImplicitlyLearnRelatedKeyScripts). } if (!HaveWatchOnly()) NotifyWatchonlyChanged(false); if (!WalletBatch(m_storage.GetDatabase()).EraseWatchOnly(dest)) return false; return true; } bool LegacyScriptPubKeyMan::LoadWatchOnly(const CScript &dest) { return AddWatchOnlyInMem(dest); } bool LegacyScriptPubKeyMan::AddWatchOnlyInMem(const CScript &dest) { LOCK(cs_KeyStore); setWatchOnly.insert(dest); CPubKey pubKey; if (ExtractPubKey(dest, pubKey)) { mapWatchKeys[pubKey.GetID()] = pubKey; ImplicitlyLearnRelatedKeyScripts(pubKey); } return true; } bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest) { if (!AddWatchOnlyInMem(dest)) return false; const CKeyMetadata& meta = m_script_metadata[CScriptID(dest)]; UpdateTimeFirstKey(meta.nCreateTime); NotifyWatchonlyChanged(true); if (batch.WriteWatchOnly(dest, meta)) { m_storage.UnsetBlankWalletFlag(batch); return true; } return false; } bool LegacyScriptPubKeyMan::AddWatchOnlyWithDB(WalletBatch &batch, const CScript& dest, int64_t create_time) { m_script_metadata[CScriptID(dest)].nCreateTime = create_time; return AddWatchOnlyWithDB(batch, dest); } bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest) { WalletBatch batch(m_storage.GetDatabase()); return AddWatchOnlyWithDB(batch, dest); } bool LegacyScriptPubKeyMan::AddWatchOnly(const CScript& dest, int64_t nCreateTime) { m_script_metadata[CScriptID(dest)].nCreateTime = nCreateTime; return AddWatchOnly(dest); } void LegacyScriptPubKeyMan::LoadHDChain(const CHDChain& chain) { LOCK(cs_KeyStore); m_hd_chain = chain; } void LegacyScriptPubKeyMan::AddHDChain(const CHDChain& chain) { LOCK(cs_KeyStore); // Store the new chain if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(chain)) { throw std::runtime_error(std::string(__func__) + ": writing chain failed"); } // When there's an old chain, add it as an inactive chain as we are now rotating hd chains if (!m_hd_chain.seed_id.IsNull()) { AddInactiveHDChain(m_hd_chain); } m_hd_chain = chain; } void LegacyScriptPubKeyMan::AddInactiveHDChain(const CHDChain& chain) { LOCK(cs_KeyStore); assert(!chain.seed_id.IsNull()); m_inactive_hd_chains[chain.seed_id] = chain; } bool LegacyScriptPubKeyMan::HaveKey(const CKeyID &address) const { LOCK(cs_KeyStore); if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::HaveKey(address); } return mapCryptedKeys.count(address) > 0; } bool LegacyScriptPubKeyMan::GetKey(const CKeyID &address, CKey& keyOut) const { LOCK(cs_KeyStore); if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::GetKey(address, keyOut); } CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { const CPubKey &vchPubKey = (*mi).second.first; const std::vector<unsigned char> &vchCryptedSecret = (*mi).second.second; return DecryptKey(m_storage.GetEncryptionKey(), vchCryptedSecret, vchPubKey, keyOut); } return false; } bool LegacyScriptPubKeyMan::GetKeyOrigin(const CKeyID& keyID, KeyOriginInfo& info) const { CKeyMetadata meta; { LOCK(cs_KeyStore); auto it = mapKeyMetadata.find(keyID); if (it != mapKeyMetadata.end()) { meta = it->second; } } if (meta.has_key_origin) { std::copy(meta.key_origin.fingerprint, meta.key_origin.fingerprint + 4, info.fingerprint); info.path = meta.key_origin.path; } else { // Single pubkeys get the master fingerprint of themselves std::copy(keyID.begin(), keyID.begin() + 4, info.fingerprint); } return true; } bool LegacyScriptPubKeyMan::GetWatchPubKey(const CKeyID &address, CPubKey &pubkey_out) const { LOCK(cs_KeyStore); WatchKeyMap::const_iterator it = mapWatchKeys.find(address); if (it != mapWatchKeys.end()) { pubkey_out = it->second; return true; } return false; } bool LegacyScriptPubKeyMan::GetPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const { LOCK(cs_KeyStore); if (!m_storage.HasEncryptionKeys()) { if (!FillableSigningProvider::GetPubKey(address, vchPubKeyOut)) { return GetWatchPubKey(address, vchPubKeyOut); } return true; } CryptedKeyMap::const_iterator mi = mapCryptedKeys.find(address); if (mi != mapCryptedKeys.end()) { vchPubKeyOut = (*mi).second.first; return true; } // Check for watch-only pubkeys return GetWatchPubKey(address, vchPubKeyOut); } CPubKey LegacyScriptPubKeyMan::GenerateNewKey(WalletBatch &batch, CHDChain& hd_chain, bool internal) { assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)); AssertLockHeld(cs_KeyStore); bool fCompressed = m_storage.CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets CKey secret; // Create new metadata int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); // use HD key derivation if HD was enabled during wallet creation and a seed is present if (IsHDEnabled()) { DeriveNewChildKey(batch, metadata, secret, hd_chain, (m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? internal : false)); } else { secret.MakeNewKey(fCompressed); } // Compressed public keys were introduced in version 0.6.0 if (fCompressed) { m_storage.SetMinVersion(FEATURE_COMPRPUBKEY); } CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); mapKeyMetadata[pubkey.GetID()] = metadata; UpdateTimeFirstKey(nCreationTime); if (!AddKeyPubKeyWithDB(batch, secret, pubkey)) { throw std::runtime_error(std::string(__func__) + ": AddKey failed"); } return pubkey; } void LegacyScriptPubKeyMan::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, CHDChain& hd_chain, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k CKey seed; //seed (256bit) CExtKey masterKey; //hd master key CExtKey accountKey; //key at m/0' CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal) CExtKey childKey; //key at m/0'/0'/<n>' // try to get the seed if (!GetKey(hd_chain.seed_id, seed)) throw std::runtime_error(std::string(__func__) + ": seed not found"); masterKey.SetSeed(seed.begin(), seed.size()); // derive m/0' // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); // derive m/0'/0' (external chain) OR m/0'/1' (internal chain) assert(internal ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true); accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT+(internal ? 1 : 0)); // derive child key at next index, skip keys already known to the wallet do { // always derive hardened keys // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649 if (internal) { chainChildKey.Derive(childKey, hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT); metadata.hdKeypath = "m/0'/1'/" + ToString(hd_chain.nInternalChainCounter) + "'"; metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); metadata.key_origin.path.push_back(1 | BIP32_HARDENED_KEY_LIMIT); metadata.key_origin.path.push_back(hd_chain.nInternalChainCounter | BIP32_HARDENED_KEY_LIMIT); hd_chain.nInternalChainCounter++; } else { chainChildKey.Derive(childKey, hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); metadata.hdKeypath = "m/0'/0'/" + ToString(hd_chain.nExternalChainCounter) + "'"; metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT); metadata.key_origin.path.push_back(hd_chain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); hd_chain.nExternalChainCounter++; } } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; metadata.hd_seed_id = hd_chain.seed_id; CKeyID master_id = masterKey.key.GetPubKey().GetID(); std::copy(master_id.begin(), master_id.begin() + 4, metadata.key_origin.fingerprint); metadata.has_key_origin = true; // update the chain model in the database if (hd_chain.seed_id == m_hd_chain.seed_id && !batch.WriteHDChain(hd_chain)) throw std::runtime_error(std::string(__func__) + ": writing HD chain model failed"); } void LegacyScriptPubKeyMan::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { LOCK(cs_KeyStore); if (keypool.m_pre_split) { set_pre_split_keypool.insert(nIndex); } else if (keypool.fInternal) { setInternalKeyPool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); } m_max_keypool_index = std::max(m_max_keypool_index, nIndex); m_pool_key_to_index[keypool.vchPubKey.GetID()] = nIndex; // If no metadata exists yet, create a default with the pool key's // creation time. Note that this may be overwritten by actually // stored metadata for that key later, which is fine. CKeyID keyid = keypool.vchPubKey.GetID(); if (mapKeyMetadata.count(keyid) == 0) mapKeyMetadata[keyid] = CKeyMetadata(keypool.nTime); } bool LegacyScriptPubKeyMan::CanGenerateKeys() const { // A wallet can generate keys if it has an HD seed (IsHDEnabled) or it is a non-HD wallet (pre FEATURE_HD) LOCK(cs_KeyStore); return IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD); } CPubKey LegacyScriptPubKeyMan::GenerateNewSeed() { assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); CKey key; key.MakeNewKey(true); return DeriveNewSeed(key); } CPubKey LegacyScriptPubKeyMan::DeriveNewSeed(const CKey& key) { int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); // calculate the seed CPubKey seed = key.GetPubKey(); assert(key.VerifyPubKey(seed)); // set the hd keypath to "s" -> Seed, refers the seed to itself metadata.hdKeypath = "s"; metadata.has_key_origin = false; metadata.hd_seed_id = seed.GetID(); { LOCK(cs_KeyStore); // mem store the metadata mapKeyMetadata[seed.GetID()] = metadata; // write the key&metadata to the database if (!AddKeyPubKey(key, seed)) throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); } return seed; } void LegacyScriptPubKeyMan::SetHDSeed(const CPubKey& seed) { LOCK(cs_KeyStore); // store the keyid (hash160) together with // the child index counter in the database // as a hdchain object CHDChain newHdChain; newHdChain.nVersion = m_storage.CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; newHdChain.seed_id = seed.GetID(); AddHDChain(newHdChain); NotifyCanGetAddressesChanged(); WalletBatch batch(m_storage.GetDatabase()); m_storage.UnsetBlankWalletFlag(batch); } /** * Mark old keypool keys as used, * and generate all new keys */ bool LegacyScriptPubKeyMan::NewKeyPool() { if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { return false; } { LOCK(cs_KeyStore); WalletBatch batch(m_storage.GetDatabase()); for (const int64_t nIndex : setInternalKeyPool) { batch.ErasePool(nIndex); } setInternalKeyPool.clear(); for (const int64_t nIndex : setExternalKeyPool) { batch.ErasePool(nIndex); } setExternalKeyPool.clear(); for (const int64_t nIndex : set_pre_split_keypool) { batch.ErasePool(nIndex); } set_pre_split_keypool.clear(); m_pool_key_to_index.clear(); if (!TopUp()) { return false; } WalletLogPrintf("LegacyScriptPubKeyMan::NewKeyPool rewrote keypool\n"); } return true; } bool LegacyScriptPubKeyMan::TopUp(unsigned int kpSize) { if (!CanGenerateKeys()) { return false; } { LOCK(cs_KeyStore); if (m_storage.IsLocked()) return false; // Top up key pool unsigned int nTargetSize; if (kpSize > 0) nTargetSize = kpSize; else nTargetSize = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0); // count amount of available keys (internal, external) // make sure the keypool of external and internal keys fits the user selected target (-keypool) int64_t missingExternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setExternalKeyPool.size(), (int64_t) 0); int64_t missingInternal = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)setInternalKeyPool.size(), (int64_t) 0); if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) { // don't create extra internal keys missingInternal = 0; } bool internal = false; WalletBatch batch(m_storage.GetDatabase()); for (int64_t i = missingInternal + missingExternal; i--;) { if (i < missingInternal) { internal = true; } CPubKey pubkey(GenerateNewKey(batch, m_hd_chain, internal)); AddKeypoolPubkeyWithDB(pubkey, internal, batch); } if (missingInternal + missingExternal > 0) { WalletLogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size()); } } NotifyCanGetAddressesChanged(); return true; } void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const bool internal, WalletBatch& batch) { LOCK(cs_KeyStore); assert(m_max_keypool_index < std::numeric_limits<int64_t>::max()); // How in the hell did you use so many keys? int64_t index = ++m_max_keypool_index; if (!batch.WritePool(index, CKeyPool(pubkey, internal))) { throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed"); } if (internal) { setInternalKeyPool.insert(index); } else { setExternalKeyPool.insert(index); } m_pool_key_to_index[pubkey.GetID()] = index; } void LegacyScriptPubKeyMan::KeepDestination(int64_t nIndex, const OutputType& type) { // Remove from key pool WalletBatch batch(m_storage.GetDatabase()); batch.ErasePool(nIndex); CPubKey pubkey; bool have_pk = GetPubKey(m_index_to_reserved_key.at(nIndex), pubkey); assert(have_pk); LearnRelatedScripts(pubkey, type); m_index_to_reserved_key.erase(nIndex); WalletLogPrintf("keypool keep %d\n", nIndex); } void LegacyScriptPubKeyMan::ReturnDestination(int64_t nIndex, bool fInternal, const CTxDestination&) { // Return to key pool { LOCK(cs_KeyStore); if (fInternal) { setInternalKeyPool.insert(nIndex); } else if (!set_pre_split_keypool.empty()) { set_pre_split_keypool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); } CKeyID& pubkey_id = m_index_to_reserved_key.at(nIndex); m_pool_key_to_index[pubkey_id] = nIndex; m_index_to_reserved_key.erase(nIndex); NotifyCanGetAddressesChanged(); } WalletLogPrintf("keypool return %d\n", nIndex); } bool LegacyScriptPubKeyMan::GetKeyFromPool(CPubKey& result, const OutputType type, bool internal) { if (!CanGetAddresses(internal)) { return false; } CKeyPool keypool; { LOCK(cs_KeyStore); int64_t nIndex; if (!ReserveKeyFromKeyPool(nIndex, keypool, internal) && !m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) { if (m_storage.IsLocked()) return false; WalletBatch batch(m_storage.GetDatabase()); result = GenerateNewKey(batch, m_hd_chain, internal); return true; } KeepDestination(nIndex, type); result = keypool.vchPubKey; } return true; } bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) { nIndex = -1; keypool.vchPubKey = CPubKey(); { LOCK(cs_KeyStore); bool fReturningInternal = fRequestedInternal; fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS); bool use_split_keypool = set_pre_split_keypool.empty(); std::set<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; // Get the oldest key if (setKeyPool.empty()) { return false; } WalletBatch batch(m_storage.GetDatabase()); auto it = setKeyPool.begin(); nIndex = *it; setKeyPool.erase(it); if (!batch.ReadPool(nIndex, keypool)) { throw std::runtime_error(std::string(__func__) + ": read failed"); } CPubKey pk; if (!GetPubKey(keypool.vchPubKey.GetID(), pk)) { throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); } // If the key was pre-split keypool, we don't care about what type it is if (use_split_keypool && keypool.fInternal != fReturningInternal) { throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); } if (!keypool.vchPubKey.IsValid()) { throw std::runtime_error(std::string(__func__) + ": keypool entry invalid"); } assert(m_index_to_reserved_key.count(nIndex) == 0); m_index_to_reserved_key[nIndex] = keypool.vchPubKey.GetID(); m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); WalletLogPrintf("keypool reserve %d\n", nIndex); } NotifyCanGetAddressesChanged(); return true; } void LegacyScriptPubKeyMan::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 LegacyScriptPubKeyMan::LearnAllRelatedScripts(const CPubKey& key) { // OutputType::P2SH_SEGWIT always adds all necessary scripts for all types. LearnRelatedScripts(key, OutputType::P2SH_SEGWIT); } void LegacyScriptPubKeyMan::MarkReserveKeysAsUsed(int64_t keypool_id) { AssertLockHeld(cs_KeyStore); bool internal = setInternalKeyPool.count(keypool_id); if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id)); std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); auto it = setKeyPool->begin(); WalletBatch batch(m_storage.GetDatabase()); while (it != std::end(*setKeyPool)) { const int64_t& index = *(it); if (index > keypool_id) break; // set*KeyPool is ordered CKeyPool keypool; if (batch.ReadPool(index, keypool)) { //TODO: This should be unnecessary m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); } LearnAllRelatedScripts(keypool.vchPubKey); batch.ErasePool(index); WalletLogPrintf("keypool index %d removed\n", index); it = setKeyPool->erase(it); } } std::vector<CKeyID> GetAffectedKeys(const CScript& spk, const SigningProvider& provider) { std::vector<CScript> dummy; FlatSigningProvider out; InferDescriptor(spk, provider)->Expand(0, DUMMY_SIGNING_PROVIDER, dummy, out); std::vector<CKeyID> ret; for (const auto& entry : out.pubkeys) { ret.push_back(entry.first); } return ret; } void LegacyScriptPubKeyMan::MarkPreSplitKeys() { WalletBatch batch(m_storage.GetDatabase()); for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { int64_t index = *it; CKeyPool keypool; if (!batch.ReadPool(index, keypool)) { throw std::runtime_error(std::string(__func__) + ": read keypool entry failed"); } keypool.m_pre_split = true; if (!batch.WritePool(index, keypool)) { throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed"); } set_pre_split_keypool.insert(index); it = setExternalKeyPool.erase(it); } } bool LegacyScriptPubKeyMan::AddCScript(const CScript& redeemScript) { WalletBatch batch(m_storage.GetDatabase()); return AddCScriptWithDB(batch, redeemScript); } bool LegacyScriptPubKeyMan::AddCScriptWithDB(WalletBatch& batch, const CScript& redeemScript) { if (!FillableSigningProvider::AddCScript(redeemScript)) return false; if (batch.WriteCScript(Hash160(redeemScript), redeemScript)) { m_storage.UnsetBlankWalletFlag(batch); return true; } return false; } bool LegacyScriptPubKeyMan::AddKeyOriginWithDB(WalletBatch& batch, const CPubKey& pubkey, const KeyOriginInfo& info) { LOCK(cs_KeyStore); std::copy(info.fingerprint, info.fingerprint + 4, mapKeyMetadata[pubkey.GetID()].key_origin.fingerprint); mapKeyMetadata[pubkey.GetID()].key_origin.path = info.path; mapKeyMetadata[pubkey.GetID()].has_key_origin = true; mapKeyMetadata[pubkey.GetID()].hdKeypath = WriteHDKeypath(info.path); return batch.WriteKeyMetadata(mapKeyMetadata[pubkey.GetID()], pubkey, true); } bool LegacyScriptPubKeyMan::ImportScripts(const std::set<CScript> scripts, int64_t timestamp) { WalletBatch batch(m_storage.GetDatabase()); for (const auto& entry : scripts) { CScriptID id(entry); if (HaveCScript(id)) { WalletLogPrintf("Already have script %s, skipping\n", HexStr(entry)); continue; } if (!AddCScriptWithDB(batch, entry)) { return false; } if (timestamp > 0) { m_script_metadata[CScriptID(entry)].nCreateTime = timestamp; } } if (timestamp > 0) { UpdateTimeFirstKey(timestamp); } return true; } bool LegacyScriptPubKeyMan::ImportPrivKeys(const std::map<CKeyID, CKey>& privkey_map, const int64_t timestamp) { WalletBatch batch(m_storage.GetDatabase()); for (const auto& entry : privkey_map) { const CKey& key = entry.second; CPubKey pubkey = key.GetPubKey(); const CKeyID& id = entry.first; assert(key.VerifyPubKey(pubkey)); // Skip if we already have the key if (HaveKey(id)) { WalletLogPrintf("Already have key with pubkey %s, skipping\n", HexStr(pubkey)); continue; } mapKeyMetadata[id].nCreateTime = timestamp; // If the private key is not present in the wallet, insert it. if (!AddKeyPubKeyWithDB(batch, key, pubkey)) { return false; } UpdateTimeFirstKey(timestamp); } return true; } bool LegacyScriptPubKeyMan::ImportPubKeys(const std::vector<CKeyID>& ordered_pubkeys, const std::map<CKeyID, CPubKey>& pubkey_map, const std::map<CKeyID, std::pair<CPubKey, KeyOriginInfo>>& key_origins, const bool add_keypool, const bool internal, const int64_t timestamp) { WalletBatch batch(m_storage.GetDatabase()); for (const auto& entry : key_origins) { AddKeyOriginWithDB(batch, entry.second.first, entry.second.second); } for (const CKeyID& id : ordered_pubkeys) { auto entry = pubkey_map.find(id); if (entry == pubkey_map.end()) { continue; } const CPubKey& pubkey = entry->second; CPubKey temp; if (GetPubKey(id, temp)) { // Already have pubkey, skipping WalletLogPrintf("Already have pubkey %s, skipping\n", HexStr(temp)); continue; } if (!AddWatchOnlyWithDB(batch, GetScriptForRawPubKey(pubkey), timestamp)) { return false; } mapKeyMetadata[id].nCreateTime = timestamp; // Add to keypool only works with pubkeys if (add_keypool) { AddKeypoolPubkeyWithDB(pubkey, internal, batch); NotifyCanGetAddressesChanged(); } } return true; } bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set<CScript>& script_pub_keys, const bool have_solving_data, const int64_t timestamp) { WalletBatch batch(m_storage.GetDatabase()); for (const CScript& script : script_pub_keys) { if (!have_solving_data || !IsMine(script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated if (!AddWatchOnlyWithDB(batch, script, timestamp)) { return false; } } } return true; } std::set<CKeyID> LegacyScriptPubKeyMan::GetKeys() const { LOCK(cs_KeyStore); if (!m_storage.HasEncryptionKeys()) { return FillableSigningProvider::GetKeys(); } std::set<CKeyID> set_address; for (const auto& mi : mapCryptedKeys) { set_address.insert(mi.first); } return set_address; } void LegacyScriptPubKeyMan::SetInternal(bool internal) {} bool DescriptorScriptPubKeyMan::GetNewDestination(const OutputType type, CTxDestination& dest, std::string& error) { // Returns true if this descriptor supports getting new addresses. Conditions where we may be unable to fetch them (e.g. locked) are caught later if (!CanGetAddresses(m_internal)) { error = "No addresses available"; return false; } { LOCK(cs_desc_man); assert(m_wallet_descriptor.descriptor->IsSingleType()); // This is a combo descriptor which should not be an active descriptor Optional<OutputType> desc_addr_type = m_wallet_descriptor.descriptor->GetOutputType(); assert(desc_addr_type); if (type != *desc_addr_type) { throw std::runtime_error(std::string(__func__) + ": Types are inconsistent"); } TopUp(); // Get the scriptPubKey from the descriptor FlatSigningProvider out_keys; std::vector<CScript> scripts_temp; if (m_wallet_descriptor.range_end <= m_max_cached_index && !TopUp(1)) { // We can't generate anymore keys error = "Error: Keypool ran out, please call keypoolrefill first"; return false; } if (!m_wallet_descriptor.descriptor->ExpandFromCache(m_wallet_descriptor.next_index, m_wallet_descriptor.cache, scripts_temp, out_keys)) { // We can't generate anymore keys error = "Error: Keypool ran out, please call keypoolrefill first"; return false; } Optional<OutputType> out_script_type = m_wallet_descriptor.descriptor->GetOutputType(); if (out_script_type && out_script_type == type) { ExtractDestination(scripts_temp[0], dest); } else { throw std::runtime_error(std::string(__func__) + ": Types are inconsistent. Stored type does not match type of newly generated address"); } m_wallet_descriptor.next_index++; WalletBatch(m_storage.GetDatabase()).WriteDescriptor(GetID(), m_wallet_descriptor); return true; } } isminetype DescriptorScriptPubKeyMan::IsMine(const CScript& script) const { LOCK(cs_desc_man); if (m_map_script_pub_keys.count(script) > 0) { return ISMINE_SPENDABLE; } return ISMINE_NO; } bool DescriptorScriptPubKeyMan::CheckDecryptionKey(const CKeyingMaterial& master_key, bool accept_no_keys) { LOCK(cs_desc_man); if (!m_map_keys.empty()) { return false; } bool keyPass = m_map_crypted_keys.empty(); // Always pass when there are no encrypted keys bool keyFail = false; for (const auto& mi : m_map_crypted_keys) { const CPubKey &pubkey = mi.second.first; const std::vector<unsigned char> &crypted_secret = mi.second.second; CKey key; if (!DecryptKey(master_key, crypted_secret, pubkey, key)) { keyFail = true; break; } keyPass = true; if (m_decryption_thoroughly_checked) break; } if (keyPass && keyFail) { LogPrintf("The wallet is probably corrupted: Some keys decrypt but not all.\n"); throw std::runtime_error("Error unlocking wallet: some keys decrypt but not all. Your wallet file may be corrupt."); } if (keyFail || (!keyPass && !accept_no_keys)) { return false; } m_decryption_thoroughly_checked = true; return true; } bool DescriptorScriptPubKeyMan::Encrypt(const CKeyingMaterial& master_key, WalletBatch* batch) { LOCK(cs_desc_man); if (!m_map_crypted_keys.empty()) { return false; } for (const KeyMap::value_type& key_in : m_map_keys) { const CKey &key = key_in.second; CPubKey pubkey = key.GetPubKey(); CKeyingMaterial secret(key.begin(), key.end()); std::vector<unsigned char> crypted_secret; if (!EncryptSecret(master_key, secret, pubkey.GetHash(), crypted_secret)) { return false; } m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret); batch->WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret); } m_map_keys.clear(); return true; } bool DescriptorScriptPubKeyMan::GetReservedDestination(const OutputType type, bool internal, CTxDestination& address, int64_t& index, CKeyPool& keypool) { LOCK(cs_desc_man); std::string error; bool result = GetNewDestination(type, address, error); index = m_wallet_descriptor.next_index - 1; return result; } void DescriptorScriptPubKeyMan::ReturnDestination(int64_t index, bool internal, const CTxDestination& addr) { LOCK(cs_desc_man); // Only return when the index was the most recent if (m_wallet_descriptor.next_index - 1 == index) { m_wallet_descriptor.next_index--; } WalletBatch(m_storage.GetDatabase()).WriteDescriptor(GetID(), m_wallet_descriptor); NotifyCanGetAddressesChanged(); } std::map<CKeyID, CKey> DescriptorScriptPubKeyMan::GetKeys() const { AssertLockHeld(cs_desc_man); if (m_storage.HasEncryptionKeys() && !m_storage.IsLocked()) { KeyMap keys; for (auto key_pair : m_map_crypted_keys) { const CPubKey& pubkey = key_pair.second.first; const std::vector<unsigned char>& crypted_secret = key_pair.second.second; CKey key; DecryptKey(m_storage.GetEncryptionKey(), crypted_secret, pubkey, key); keys[pubkey.GetID()] = key; } return keys; } return m_map_keys; } bool DescriptorScriptPubKeyMan::TopUp(unsigned int size) { LOCK(cs_desc_man); unsigned int target_size; if (size > 0) { target_size = size; } else { target_size = std::max(gArgs.GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 1); } // Calculate the new range_end int32_t new_range_end = std::max(m_wallet_descriptor.next_index + (int32_t)target_size, m_wallet_descriptor.range_end); // If the descriptor is not ranged, we actually just want to fill the first cache item if (!m_wallet_descriptor.descriptor->IsRange()) { new_range_end = 1; m_wallet_descriptor.range_end = 1; m_wallet_descriptor.range_start = 0; } FlatSigningProvider provider; provider.keys = GetKeys(); WalletBatch batch(m_storage.GetDatabase()); uint256 id = GetID(); for (int32_t i = m_max_cached_index + 1; i < new_range_end; ++i) { FlatSigningProvider out_keys; std::vector<CScript> scripts_temp; DescriptorCache temp_cache; // Maybe we have a cached xpub and we can expand from the cache first if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) { if (!m_wallet_descriptor.descriptor->Expand(i, provider, scripts_temp, out_keys, &temp_cache)) return false; } // Add all of the scriptPubKeys to the scriptPubKey set for (const CScript& script : scripts_temp) { m_map_script_pub_keys[script] = i; } for (const auto& pk_pair : out_keys.pubkeys) { const CPubKey& pubkey = pk_pair.second; if (m_map_pubkeys.count(pubkey) != 0) { // We don't need to give an error here. // It doesn't matter which of many valid indexes the pubkey has, we just need an index where we can derive it and it's private key continue; } m_map_pubkeys[pubkey] = i; } // Write the cache for (const auto& parent_xpub_pair : temp_cache.GetCachedParentExtPubKeys()) { CExtPubKey xpub; if (m_wallet_descriptor.cache.GetCachedParentExtPubKey(parent_xpub_pair.first, xpub)) { if (xpub != parent_xpub_pair.second) { throw std::runtime_error(std::string(__func__) + ": New cached parent xpub does not match already cached parent xpub"); } continue; } if (!batch.WriteDescriptorParentCache(parent_xpub_pair.second, id, parent_xpub_pair.first)) { throw std::runtime_error(std::string(__func__) + ": writing cache item failed"); } m_wallet_descriptor.cache.CacheParentExtPubKey(parent_xpub_pair.first, parent_xpub_pair.second); } for (const auto& derived_xpub_map_pair : temp_cache.GetCachedDerivedExtPubKeys()) { for (const auto& derived_xpub_pair : derived_xpub_map_pair.second) { CExtPubKey xpub; if (m_wallet_descriptor.cache.GetCachedDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, xpub)) { if (xpub != derived_xpub_pair.second) { throw std::runtime_error(std::string(__func__) + ": New cached derived xpub does not match already cached derived xpub"); } continue; } if (!batch.WriteDescriptorDerivedCache(derived_xpub_pair.second, id, derived_xpub_map_pair.first, derived_xpub_pair.first)) { throw std::runtime_error(std::string(__func__) + ": writing cache item failed"); } m_wallet_descriptor.cache.CacheDerivedExtPubKey(derived_xpub_map_pair.first, derived_xpub_pair.first, derived_xpub_pair.second); } } m_max_cached_index++; } m_wallet_descriptor.range_end = new_range_end; batch.WriteDescriptor(GetID(), m_wallet_descriptor); // By this point, the cache size should be the size of the entire range assert(m_wallet_descriptor.range_end - 1 == m_max_cached_index); NotifyCanGetAddressesChanged(); return true; } void DescriptorScriptPubKeyMan::MarkUnusedAddresses(const CScript& script) { LOCK(cs_desc_man); if (IsMine(script)) { int32_t index = m_map_script_pub_keys[script]; if (index >= m_wallet_descriptor.next_index) { WalletLogPrintf("%s: Detected a used keypool item at index %d, mark all keypool items up to this item as used\n", __func__, index); m_wallet_descriptor.next_index = index + 1; } if (!TopUp()) { WalletLogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); } } } void DescriptorScriptPubKeyMan::AddDescriptorKey(const CKey& key, const CPubKey &pubkey) { LOCK(cs_desc_man); WalletBatch batch(m_storage.GetDatabase()); if (!AddDescriptorKeyWithDB(batch, key, pubkey)) { throw std::runtime_error(std::string(__func__) + ": writing descriptor private key failed"); } } bool DescriptorScriptPubKeyMan::AddDescriptorKeyWithDB(WalletBatch& batch, const CKey& key, const CPubKey &pubkey) { AssertLockHeld(cs_desc_man); assert(!m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)); if (m_storage.HasEncryptionKeys()) { if (m_storage.IsLocked()) { return false; } std::vector<unsigned char> crypted_secret; CKeyingMaterial secret(key.begin(), key.end()); if (!EncryptSecret(m_storage.GetEncryptionKey(), secret, pubkey.GetHash(), crypted_secret)) { return false; } m_map_crypted_keys[pubkey.GetID()] = make_pair(pubkey, crypted_secret); return batch.WriteCryptedDescriptorKey(GetID(), pubkey, crypted_secret); } else { m_map_keys[pubkey.GetID()] = key; return batch.WriteDescriptorKey(GetID(), pubkey, key.GetPrivKey()); } } bool DescriptorScriptPubKeyMan::SetupDescriptorGeneration(const CExtKey& master_key, OutputType addr_type) { LOCK(cs_desc_man); assert(m_storage.IsWalletFlagSet(WALLET_FLAG_DESCRIPTORS)); // Ignore when there is already a descriptor if (m_wallet_descriptor.descriptor) { return false; } int64_t creation_time = GetTime(); std::string xpub = EncodeExtPubKey(master_key.Neuter()); // Build descriptor string std::string desc_prefix; std::string desc_suffix = "/*)"; switch (addr_type) { case OutputType::LEGACY: { desc_prefix = "pkh(" + xpub + "/44'"; break; } case OutputType::P2SH_SEGWIT: { desc_prefix = "sh(wpkh(" + xpub + "/49'"; desc_suffix += ")"; break; } case OutputType::BECH32: { desc_prefix = "wpkh(" + xpub + "/84'"; break; } } // no default case, so the compiler can warn about missing cases assert(!desc_prefix.empty()); // Mainnet derives at 0', testnet and regtest derive at 1' if (Params().IsTestChain()) { desc_prefix += "/1'"; } else { desc_prefix += "/0'"; } std::string internal_path = m_internal ? "/1" : "/0"; std::string desc_str = desc_prefix + "/0'" + internal_path + desc_suffix; // Make the descriptor FlatSigningProvider keys; std::string error; std::unique_ptr<Descriptor> desc = Parse(desc_str, keys, error, false); WalletDescriptor w_desc(std::move(desc), creation_time, 0, 0, 0); m_wallet_descriptor = w_desc; // Store the master private key, and descriptor WalletBatch batch(m_storage.GetDatabase()); if (!AddDescriptorKeyWithDB(batch, master_key.key, master_key.key.GetPubKey())) { throw std::runtime_error(std::string(__func__) + ": writing descriptor master private key failed"); } if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) { throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); } // TopUp TopUp(); m_storage.UnsetBlankWalletFlag(batch); return true; } bool DescriptorScriptPubKeyMan::IsHDEnabled() const { LOCK(cs_desc_man); return m_wallet_descriptor.descriptor->IsRange(); } bool DescriptorScriptPubKeyMan::CanGetAddresses(bool internal) const { // We can only give out addresses from descriptors that are single type (not combo), ranged, // and either have cached keys or can generate more keys (ignoring encryption) LOCK(cs_desc_man); return m_wallet_descriptor.descriptor->IsSingleType() && m_wallet_descriptor.descriptor->IsRange() && (HavePrivateKeys() || m_wallet_descriptor.next_index < m_wallet_descriptor.range_end); } bool DescriptorScriptPubKeyMan::HavePrivateKeys() const { LOCK(cs_desc_man); return m_map_keys.size() > 0 || m_map_crypted_keys.size() > 0; } int64_t DescriptorScriptPubKeyMan::GetOldestKeyPoolTime() const { // This is only used for getwalletinfo output and isn't relevant to descriptor wallets. // The magic number 0 indicates that it shouldn't be displayed so that's what we return. return 0; } size_t DescriptorScriptPubKeyMan::KeypoolCountExternalKeys() const { if (m_internal) { return 0; } return GetKeyPoolSize(); } unsigned int DescriptorScriptPubKeyMan::GetKeyPoolSize() const { LOCK(cs_desc_man); return m_wallet_descriptor.range_end - m_wallet_descriptor.next_index; } int64_t DescriptorScriptPubKeyMan::GetTimeFirstKey() const { LOCK(cs_desc_man); return m_wallet_descriptor.creation_time; } std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(const CScript& script, bool include_private) const { LOCK(cs_desc_man); // Find the index of the script auto it = m_map_script_pub_keys.find(script); if (it == m_map_script_pub_keys.end()) { return nullptr; } int32_t index = it->second; return GetSigningProvider(index, include_private); } std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(const CPubKey& pubkey) const { LOCK(cs_desc_man); // Find index of the pubkey auto it = m_map_pubkeys.find(pubkey); if (it == m_map_pubkeys.end()) { return nullptr; } int32_t index = it->second; // Always try to get the signing provider with private keys. This function should only be called during signing anyways return GetSigningProvider(index, true); } std::unique_ptr<FlatSigningProvider> DescriptorScriptPubKeyMan::GetSigningProvider(int32_t index, bool include_private) const { AssertLockHeld(cs_desc_man); // Get the scripts, keys, and key origins for this script std::unique_ptr<FlatSigningProvider> out_keys = MakeUnique<FlatSigningProvider>(); std::vector<CScript> scripts_temp; if (!m_wallet_descriptor.descriptor->ExpandFromCache(index, m_wallet_descriptor.cache, scripts_temp, *out_keys)) return nullptr; if (HavePrivateKeys() && include_private) { FlatSigningProvider master_provider; master_provider.keys = GetKeys(); m_wallet_descriptor.descriptor->ExpandPrivate(index, master_provider, *out_keys); } return out_keys; } std::unique_ptr<SigningProvider> DescriptorScriptPubKeyMan::GetSolvingProvider(const CScript& script) const { return GetSigningProvider(script, false); } bool DescriptorScriptPubKeyMan::CanProvide(const CScript& script, SignatureData& sigdata) { return IsMine(script); } bool DescriptorScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const { std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>(); for (const auto& coin_pair : coins) { std::unique_ptr<FlatSigningProvider> coin_keys = GetSigningProvider(coin_pair.second.out.scriptPubKey, true); if (!coin_keys) { continue; } *keys = Merge(*keys, *coin_keys); } return ::SignTransaction(tx, keys.get(), coins, sighash, input_errors); } SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { std::unique_ptr<FlatSigningProvider> keys = GetSigningProvider(GetScriptForDestination(pkhash), true); if (!keys) { return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; } CKey key; if (!keys->GetKey(ToKeyID(pkhash), key)) { return SigningResult::PRIVATE_KEY_NOT_AVAILABLE; } if (!MessageSign(key, message, str_sig)) { return SigningResult::SIGNING_FAILED; } return SigningResult::OK; } TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const { if (n_signed) { *n_signed = 0; } for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) { const CTxIn& txin = psbtx.tx->vin[i]; PSBTInput& input = psbtx.inputs.at(i); if (PSBTInputSigned(input)) { continue; } // Get the Sighash type if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) { return TransactionError::SIGHASH_MISMATCH; } // Get the scriptPubKey to know which SigningProvider to use CScript script; if (!input.witness_utxo.IsNull()) { script = input.witness_utxo.scriptPubKey; } else if (input.non_witness_utxo) { if (txin.prevout.n >= input.non_witness_utxo->vout.size()) { return TransactionError::MISSING_INPUTS; } script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey; } else { // There's no UTXO so we can just skip this now continue; } SignatureData sigdata; input.FillSignatureData(sigdata); std::unique_ptr<FlatSigningProvider> keys = MakeUnique<FlatSigningProvider>(); std::unique_ptr<FlatSigningProvider> script_keys = GetSigningProvider(script, sign); if (script_keys) { *keys = Merge(*keys, *script_keys); } else { // Maybe there are pubkeys listed that we can sign for script_keys = MakeUnique<FlatSigningProvider>(); for (const auto& pk_pair : input.hd_keypaths) { const CPubKey& pubkey = pk_pair.first; std::unique_ptr<FlatSigningProvider> pk_keys = GetSigningProvider(pubkey); if (pk_keys) { *keys = Merge(*keys, *pk_keys); } } } SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type); bool signed_one = PSBTInputSigned(input); if (n_signed && (signed_one || !sign)) { // If sign is false, we assume that we _could_ sign if we get here. This // will never have false negatives; it is hard to tell under what i // circumstances it could have false positives. (*n_signed)++; } } // Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) { std::unique_ptr<SigningProvider> keys = GetSolvingProvider(psbtx.tx->vout.at(i).scriptPubKey); if (!keys) { continue; } UpdatePSBTOutput(HidingSigningProvider(keys.get(), true, !bip32derivs), psbtx, i); } return TransactionError::OK; } std::unique_ptr<CKeyMetadata> DescriptorScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const { std::unique_ptr<SigningProvider> provider = GetSigningProvider(GetScriptForDestination(dest)); if (provider) { KeyOriginInfo orig; CKeyID key_id = GetKeyForDestination(*provider, dest); if (provider->GetKeyOrigin(key_id, orig)) { LOCK(cs_desc_man); std::unique_ptr<CKeyMetadata> meta = MakeUnique<CKeyMetadata>(); meta->key_origin = orig; meta->has_key_origin = true; meta->nCreateTime = m_wallet_descriptor.creation_time; return meta; } } return nullptr; } uint256 DescriptorScriptPubKeyMan::GetID() const { LOCK(cs_desc_man); std::string desc_str = m_wallet_descriptor.descriptor->ToString(); uint256 id; CSHA256().Write((unsigned char*)desc_str.data(), desc_str.size()).Finalize(id.begin()); return id; } void DescriptorScriptPubKeyMan::SetInternal(bool internal) { this->m_internal = internal; } void DescriptorScriptPubKeyMan::SetCache(const DescriptorCache& cache) { LOCK(cs_desc_man); m_wallet_descriptor.cache = cache; for (int32_t i = m_wallet_descriptor.range_start; i < m_wallet_descriptor.range_end; ++i) { FlatSigningProvider out_keys; std::vector<CScript> scripts_temp; if (!m_wallet_descriptor.descriptor->ExpandFromCache(i, m_wallet_descriptor.cache, scripts_temp, out_keys)) { throw std::runtime_error("Error: Unable to expand wallet descriptor from cache"); } // Add all of the scriptPubKeys to the scriptPubKey set for (const CScript& script : scripts_temp) { if (m_map_script_pub_keys.count(script) != 0) { throw std::runtime_error(strprintf("Error: Already loaded script at index %d as being at index %d", i, m_map_script_pub_keys[script])); } m_map_script_pub_keys[script] = i; } for (const auto& pk_pair : out_keys.pubkeys) { const CPubKey& pubkey = pk_pair.second; if (m_map_pubkeys.count(pubkey) != 0) { // We don't need to give an error here. // It doesn't matter which of many valid indexes the pubkey has, we just need an index where we can derive it and it's private key continue; } m_map_pubkeys[pubkey] = i; } m_max_cached_index++; } } bool DescriptorScriptPubKeyMan::AddKey(const CKeyID& key_id, const CKey& key) { LOCK(cs_desc_man); m_map_keys[key_id] = key; return true; } bool DescriptorScriptPubKeyMan::AddCryptedKey(const CKeyID& key_id, const CPubKey& pubkey, const std::vector<unsigned char>& crypted_key) { LOCK(cs_desc_man); if (!m_map_keys.empty()) { return false; } m_map_crypted_keys[key_id] = make_pair(pubkey, crypted_key); return true; } bool DescriptorScriptPubKeyMan::HasWalletDescriptor(const WalletDescriptor& desc) const { LOCK(cs_desc_man); return m_wallet_descriptor.descriptor != nullptr && desc.descriptor != nullptr && m_wallet_descriptor.descriptor->ToString() == desc.descriptor->ToString(); } void DescriptorScriptPubKeyMan::WriteDescriptor() { LOCK(cs_desc_man); WalletBatch batch(m_storage.GetDatabase()); if (!batch.WriteDescriptor(GetID(), m_wallet_descriptor)) { throw std::runtime_error(std::string(__func__) + ": writing descriptor failed"); } } const WalletDescriptor DescriptorScriptPubKeyMan::GetWalletDescriptor() const { return m_wallet_descriptor; } const std::vector<CScript> DescriptorScriptPubKeyMan::GetScriptPubKeys() const { LOCK(cs_desc_man); std::vector<CScript> script_pub_keys; script_pub_keys.reserve(m_map_script_pub_keys.size()); for (auto const& script_pub_key: m_map_script_pub_keys) { script_pub_keys.push_back(script_pub_key.first); } return script_pub_keys; } bool DescriptorScriptPubKeyMan::GetDescriptorString(std::string& out, bool priv) const { LOCK(cs_desc_man); if (m_storage.IsLocked()) { return false; } FlatSigningProvider provider; provider.keys = GetKeys(); return m_wallet_descriptor.descriptor->ToNormalizedString(provider, out, priv); }