git / moonbite-core / src/wallet/scriptpubkeyman.cpp · raw

// 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 <outputtype.h>#include <script/descriptor.h>#include <script/sign.h>#include <util/bip32.h>#include <util/strencodings.h>#include <util/string.h>#include <util/translation.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;static KeyPurpose GetPurpose(const OutputType type, const bool internal){    if (type == OutputType::MWEB) {        return KeyPurpose::MWEB;    }    return internal ? KeyPurpose::INTERNAL : KeyPurpose::EXTERNAL;}static const char* GetPurposeDesc(const KeyPurpose purpose){    switch (purpose) {    case KeyPurpose::EXTERNAL: return "external";    case KeyPurpose::INTERNAL: return "internal";    case KeyPurpose::MWEB: return "mweb";    }    assert(false);}static uint32_t* GetChainCounter(CHDChain& chain, const KeyPurpose purpose){    switch (purpose) {    case KeyPurpose::EXTERNAL: return &chain.nExternalChainCounter;    case KeyPurpose::INTERNAL: return &chain.nInternalChainCounter;    case KeyPurpose::MWEB: return &chain.nMWEBIndexCounter;    }    assert(false);};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, GetScanSecret());    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 spendableIsMineResult 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:    case TxoutType::WITNESS_MWEB_PEGIN:    case TxoutType::WITNESS_MWEB_HOGADDR:        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;    }    }    if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) {        ret = std::max(ret, IsMineResult::WATCH_ONLY);    }    return ret;}} // namespaceisminetype LegacyScriptPubKeyMan::IsMine(const DestinationAddr& script) const{    if (script.IsMWEB()) {        if (GetScanSecret().IsNull()) {            return ISMINE_NO;        }        const StealthAddress& mweb_address = script.GetMWEBAddress();        if (mweb_address.GetSpendPubKey().Mul(GetScanSecret()) != mweb_address.GetScanPubKey()) {            return ISMINE_NO;        }        return HaveKey(mweb_address.GetSpendPubKey().GetID()) ? ISMINE_SPENDABLE : ISMINE_NO;    }    switch (IsMineInner(*this, script.GetScript(), 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(GetPurpose(type, internal))) {        return false;    }    if (!ReserveKeyFromKeyPool(index, keypool, GetPurpose(type, internal))) {        return false;    }    address = GetDestinationForKey(keypool.vchPubKey, type, GetScanSecret());    return true;}bool LegacyScriptPubKeyMan::TopUpInactiveHDChain(const CKeyID seed_id, int64_t index, const KeyPurpose purpose){    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 = ((int64_t)*GetChainCounter(chain, purpose)) - (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, purpose);        }        WalletLogPrintf("inactive seed with id %s added %d %s keys\n", HexStr(seed_id), missing, GetPurposeDesc(purpose));    }    return true;}void LegacyScriptPubKeyMan::MarkUnusedAddresses(const DestinationAddr& 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) {                KeyPurpose purpose = KeyPurpose::EXTERNAL;                if (!!meta.mweb_index) {                    purpose = KeyPurpose::MWEB;                } else if ((meta.key_origin.path[1] & ~BIP32_HARDENED_KEY_LIMIT) != 0) {                    purpose = KeyPurpose::INTERNAL;                }                int64_t index = !!meta.mweb_index ? *meta.mweb_index : (meta.key_origin.path[2] & ~BIP32_HARDENED_KEY_LIMIT);                if (!TopUpInactiveHDChain(meta.hd_seed_id, index, purpose)) {                    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(const KeyPurpose purpose) const{    LOCK(cs_KeyStore);    // Check if the keypool has keys    bool keypool_has_keys;    if (purpose == KeyPurpose::INTERNAL && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {        keypool_has_keys = setInternalKeyPool.size() > 0;    } else if (purpose == KeyPurpose::MWEB) {        if (!m_mwebKeychain) {            return false;        }        keypool_has_keys = set_mweb_keypool.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_MWEB) {            m_hd_chain.nVersion = CHDChain::VERSION_HD_MWEB;            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();    set_mweb_keypool.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);        }        if (!set_mweb_keypool.empty()) {            oldestKey = std::max(GetOldestKeyTimeInPool(set_mweb_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 DestinationAddr& dest_addr) const{    return MakeUnique<LegacySigningProvider>(*this);}bool LegacyScriptPubKeyMan::CanProvide(const DestinationAddr& dest_addr, SignatureData& sigdata){    if (dest_addr.IsMWEB()) {        return IsMine(dest_addr) & isminetype::ISMINE_SPENDABLE;    }    IsMineResult ismine = IsMineInner(*this, dest_addr.GetScript(), 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, dest_addr.GetScript(), 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, const KeyPurpose purpose){    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) ? purpose : KeyPurpose::EXTERNAL);    } 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, const KeyPurpose purpose){    // 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)    // try to get the seed    if (!GetKey(hd_chain.seed_id, seed))        throw std::runtime_error(std::string(__func__) + ": seed not found");    if (purpose == KeyPurpose::MWEB && m_mwebKeychain == nullptr) {        throw std::runtime_error(std::string(__func__) + ": MWEB keychain 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(purpose == KeyPurpose::INTERNAL ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true);    assert(purpose == KeyPurpose::MWEB ? m_storage.CanSupportFeature(FEATURE_HD_SPLIT) : true);    accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT + (uint32_t)purpose);    // derive child key at next index, skip keys already known to the wallet    uint32_t& chain_counter = *GetChainCounter(hd_chain, purpose);    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 (purpose == KeyPurpose::MWEB) {            SecretKey spend_key = m_mwebKeychain->GetSpendKey(chain_counter);            secret.Set(spend_key.vec().begin(), spend_key.vec().end(), true);            metadata.mweb_index = chain_counter;            metadata.hdKeypath = "x/" + ToString(chain_counter);            metadata.key_origin.path.push_back(chain_counter);        } else {            CExtKey childKey; //key at m/0'/0'/<n>'            chainChildKey.Derive(childKey, chain_counter | BIP32_HARDENED_KEY_LIMIT);            metadata.hdKeypath = "m/0'/" + ToString((uint32_t)purpose) + "'/" + ToString(chain_counter) + "'";            metadata.key_origin.path.push_back(0 | BIP32_HARDENED_KEY_LIMIT);            metadata.key_origin.path.push_back((uint32_t)purpose | BIP32_HARDENED_KEY_LIMIT);            metadata.key_origin.path.push_back(chain_counter | BIP32_HARDENED_KEY_LIMIT);            secret = childKey.key;        }        chain_counter++;    } while (HaveKey(secret.GetPubKey().GetID()));    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.fMWEB) {        set_mweb_keypool.insert(nIndex);    } else 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_MWEB : CHDChain::VERSION_HD_BASE;    newHdChain.seed_id = seed.GetID();    AddHDChain(newHdChain);    LoadMWEBKeychain();    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();        for (const int64_t nIndex : set_mweb_keypool) {            batch.ErasePool(nIndex);        }        set_mweb_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);        int64_t missingMWEB = std::max(std::max((int64_t) nTargetSize, (int64_t) 1) - (int64_t)set_mweb_keypool.size(), (int64_t) 0);        if (!IsHDEnabled() || !m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {            // don't create extra internal keys            missingInternal = 0;        }        if (m_mwebKeychain == nullptr) {            missingMWEB = 0;        }        WalletBatch batch(m_storage.GetDatabase());        for (int64_t i = missingInternal + missingExternal + missingMWEB; i--;)        {            KeyPurpose purpose = KeyPurpose::EXTERNAL;            if (i < missingInternal) {                purpose = KeyPurpose::INTERNAL;            } else if (i < (missingInternal + missingMWEB)) {                purpose = KeyPurpose::MWEB;            }            CPubKey pubkey(GenerateNewKey(batch, m_hd_chain, purpose));            AddKeypoolPubkeyWithDB(pubkey, purpose, batch);        }        if (missingInternal + missingExternal + missingMWEB > 0) {            WalletLogPrintf("keypool added %d keys (%d internal, %d MWEB), size=%u (%u internal, %u MWEB)\n", missingInternal + missingExternal + missingMWEB, missingInternal, missingMWEB, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size() + set_mweb_keypool.size(), setInternalKeyPool.size(), set_mweb_keypool.size());        }    }    NotifyCanGetAddressesChanged();    return true;}void LegacyScriptPubKeyMan::AddKeypoolPubkeyWithDB(const CPubKey& pubkey, const KeyPurpose purpose, 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, purpose == KeyPurpose::INTERNAL, purpose == KeyPurpose::MWEB))) {        throw std::runtime_error(std::string(__func__) + ": writing imported pubkey failed");    }    if (purpose == KeyPurpose::INTERNAL) {        setInternalKeyPool.insert(index);    } else if (purpose == KeyPurpose::MWEB) {        set_mweb_keypool.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, const KeyPurpose purpose, const CTxDestination&){    // Return to key pool    {        LOCK(cs_KeyStore);        if (purpose == KeyPurpose::INTERNAL) {            setInternalKeyPool.insert(nIndex);        } else if (purpose == KeyPurpose::MWEB) {            set_mweb_keypool.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(GetPurpose(type, internal))) {        return false;    }    CKeyPool keypool;    {        LOCK(cs_KeyStore);        int64_t nIndex;        if (!ReserveKeyFromKeyPool(nIndex, keypool, GetPurpose(type, 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, GetPurpose(type, internal));            return true;        }        KeepDestination(nIndex, type);        result = keypool.vchPubKey;    }    return true;}bool LegacyScriptPubKeyMan::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, const KeyPurpose purpose){    nIndex = -1;    keypool.vchPubKey = CPubKey();    {        LOCK(cs_KeyStore);        bool fReturningInternal = (purpose == KeyPurpose::INTERNAL);        fReturningInternal &= (IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) || m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);        bool fMWEB = (purpose == KeyPurpose::MWEB) && IsHDEnabled() && m_storage.CanSupportFeature(FEATURE_HD_SPLIT);        bool use_pre_split = !fMWEB && !set_pre_split_keypool.empty();        auto fn_get_keypool = [this](const bool internal, const bool mweb) -> std::set<int64_t>& {             if (mweb) {                return set_mweb_keypool;             } else if (!set_pre_split_keypool.empty()) {                 return set_pre_split_keypool;             } else if (internal) {                return setInternalKeyPool;            }            return setExternalKeyPool;        };        std::set<int64_t>& setKeyPool = fn_get_keypool(fReturningInternal, fMWEB);        // 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 (keypool.fMWEB != fMWEB) {            throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified");        }        // If the key was pre-split keypool, we don't care about what type it is        if (!use_pre_split && 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 mweb = set_mweb_keypool.count(keypool_id);    bool internal = !mweb && setInternalKeyPool.count(keypool_id);    if (!internal && !mweb) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id));    std::set<int64_t>* setKeyPool = nullptr;    if (mweb) {        setKeyPool = &set_mweb_keypool;    } else if (internal) {        setKeyPool = &setInternalKeyPool;    } else {        setKeyPool = (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());        }        if (!mweb) {            LearnAllRelatedScripts(keypool.vchPubKey);        }        batch.ErasePool(index);        WalletLogPrintf("keypool index %d removed\n", index);        it = setKeyPool->erase(it);    }}std::vector<CKeyID> GetAffectedKeys(const DestinationAddr& spk, const SigningProvider& provider){    if (spk.IsMWEB()) {        return std::vector<CKeyID>{spk.GetMWEBAddress().GetSpendPubKey().GetID()};    }    std::vector<DestinationAddr> 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 ? KeyPurpose::INTERNAL : KeyPurpose::EXTERNAL, batch);            NotifyCanGetAddressesChanged();        }    }    return true;}bool LegacyScriptPubKeyMan::ImportScriptPubKeys(const std::set<DestinationAddr>& script_pub_keys, const bool have_solving_data, const int64_t timestamp){    WalletBatch batch(m_storage.GetDatabase());    for (const DestinationAddr& script : script_pub_keys) {        if (script.IsMWEB()) {            continue;        }        if (!have_solving_data || !IsMine(script)) { // Always call AddWatchOnly for non-solvable watch-only, so that watch timestamp gets updated            if (!AddWatchOnlyWithDB(batch, script.GetScript(), 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) {}void LegacyScriptPubKeyMan::LoadMWEBKeychain(){    if (!m_storage.CanSupportFeature(FEATURE_HD_SPLIT)) {        return;    }    if (m_storage.IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) || m_storage.IsWalletFlagSet(WALLET_FLAG_BLANK_WALLET)) {        return;    }    m_storage.SetMinVersion(FEATURE_MWEB);    // try to get the seed    CKey seed;    if (!GetKey(m_hd_chain.seed_id, seed)) {        if (m_hd_chain.mweb_scan_key) {            m_mwebKeychain = std::make_shared<mw::Keychain>(                *this,                *m_hd_chain.mweb_scan_key,                SecretKey::Null()            );        }        return;    }    CExtKey masterKey;    masterKey.SetSeed(seed.begin(), seed.size());    // derive m/0'    CExtKey accountKey;    masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);    // derive m/0'/100' (MWEB)    CExtKey chainChildKey;    accountKey.Derive(chainChildKey, BIP32_HARDENED_KEY_LIMIT + (uint32_t)KeyPurpose::MWEB);    CExtKey scanKey;    chainChildKey.Derive(scanKey, BIP32_HARDENED_KEY_LIMIT);    CExtKey spendKey;    chainChildKey.Derive(spendKey, BIP32_HARDENED_KEY_LIMIT + 1);    m_hd_chain.nVersion = std::max(m_hd_chain.nVersion, CHDChain::VERSION_HD_MWEB_WATCH);    m_mwebKeychain = std::make_shared<mw::Keychain>(        *this,        SecretKey(scanKey.key.begin()),        SecretKey(spendKey.key.begin())    );    // Add the MWEB scan key to the CHDChain    if (!m_hd_chain.mweb_scan_key) {        m_hd_chain.mweb_scan_key = SecretKey(scanKey.key.begin());        if (!WalletBatch(m_storage.GetDatabase()).WriteHDChain(m_hd_chain)) {            throw std::runtime_error(std::string(__func__) + ": writing chain failed");        }    }    // Mark change and peg-in addresses as used    if (m_hd_chain.nMWEBIndexCounter == 0) {        WalletBatch batch(m_storage.GetDatabase());        // Generate CHANGE pubkey        GenerateNewKey(batch, m_hd_chain, KeyPurpose::MWEB);        // Generate PEGIN pubkey        GenerateNewKey(batch, m_hd_chain, KeyPurpose::MWEB);    }}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(GetPurpose(type, 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<DestinationAddr> 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) {            scripts_temp[0].ExtractDestination(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 DestinationAddr& 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, const KeyPurpose purpose, 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<DestinationAddr> 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 DestinationAddr& 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 DestinationAddr& 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;    }    case OutputType::MWEB: {        desc_prefix = "mweb(" + xpub + "/100'";        break;    }    } // no default case, so the compiler can warn about missing cases    assert(!desc_prefix.empty());    // Mainnet derives at 2', testnet and regtest derive at 1'    if (Params().IsTestChain()) {        desc_prefix += "/1'";    } else {        desc_prefix += "/2'";    }    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");    }    if (addr_type == OutputType::MWEB) {        LoadMWEBKeychain();    }    // 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(const KeyPurpose /*purpose*/) 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<DestinationAddr> 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 DestinationAddr& dest_addr) const{    if (dest_addr.IsMWEB()) {        return nullptr;    }    return GetSigningProvider(dest_addr.GetScript(), false);}bool DescriptorScriptPubKeyMan::CanProvide(const DestinationAddr& dest_addr, SignatureData& sigdata){    return IsMine(dest_addr);}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<DestinationAddr> 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 DestinationAddr& 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<DestinationAddr> DescriptorScriptPubKeyMan::GetScriptPubKeys() const{    LOCK(cs_desc_man);    std::vector<DestinationAddr> 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;}