<NME> wallet.cpp <BEF> // Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "wallet.h" #include "walletexcept.h" #include "sigmaspendbuilder.h" #include "lelantusjoinsplitbuilder.h" #include "amount.h" #include "base58.h" #include "checkpoints.h" #include "chain.h" #include "wallet/coincontrol.h" #include "consensus/consensus.h" #include "consensus/validation.h" #include "key.h" #include "keystore.h" #include "validation.h" #include "sigma.h" #include "../sigma/coinspend.h" #include "../sigma/spend_metadata.h" #include "../sigma/coin.h" #include "lelantus.h" #include "llmq/quorums_instantsend.h" #include "llmq/quorums_chainlocks.h" #include "net.h" #include "policy/policy.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "script/script.h" #include "script/sign.h" #include "timedata.h" #include "txmempool.h" #include "util.h" #include "ui_interface.h" #include "utilmoneystr.h" #include "validation.h" #include "masternode-sync.h" #include "random.h" #include "init.h" #include "hdmint/wallet.h" #include "rpc/protocol.h" #include "crypto/hmac_sha512.h" #include "crypto/aes.h" #include "hdmint/tracker.h" #include "evo/deterministicmns.h" #include <assert.h> #include <boost/algorithm/string.hpp> #include <boost/algorithm/string/replace.hpp> #include <boost/filesystem.hpp> #include <boost/thread.hpp> #include <vector> #include "bip47/account.h" #include "bip47/paymentcode.h" #include "bip47/bip47utils.h" CWallet* pwalletMain = NULL; /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE; bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS; bool fWalletRbf = DEFAULT_WALLET_RBF; const char * DEFAULT_WALLET_DAT = "wallet.dat"; /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) * Override with -mintxfee */ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE); /** * If fee estimation does not have enough data to provide estimates, use this fee instead. * Has no effect if not using fee estimation * Override with -fallbackfee */ CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE); const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001")); /** @defgroup mapWallet * * @{ */ struct CompareValueOnly { bool operator()(const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t1, const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t2) const { return t1.first < t2.first; } }; struct CompareByAmount { bool operator()(const CompactTallyItem& t1, const CompactTallyItem& t2) const { return t1.nAmount > t2.nAmount; } }; static void EnsureMintWalletAvailable() { if (!pwalletMain || !pwalletMain->zwallet) { throw std::logic_error("Sigma feature requires HD wallet"); } } std::string COutput::ToString() const { return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue)); } class CAffectedKeysVisitor : public boost::static_visitor<void> { private: const CKeyStore &keystore; std::vector<CKeyID> &vKeys; public: CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {} void Process(const CScript &script) { txnouttype type; std::vector<CTxDestination> vDest; int nRequired; if (ExtractDestinations(script, type, vDest, nRequired)) { BOOST_FOREACH(const CTxDestination &dest, vDest) boost::apply_visitor(*this, dest); } } void operator()(const CKeyID &keyId) { if (keystore.HaveKey(keyId)) vKeys.push_back(keyId); } void operator()(const CScriptID &scriptId) { CScript script; if (keystore.GetCScript(scriptId, script)) Process(script); } void operator()(const CNoDestination &none) {} }; const CWalletTx *CWallet::GetWalletTx(const uint256 &hash) const { LOCK(cs_wallet); std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash); if (it == mapWallet.end()) return NULL; return &(it->second); } CPubKey CWallet::GetKeyFromKeypath(uint32_t nChange, uint32_t nChild, CKey& secret) { AssertLockHeld(cs_wallet); // mapKeyMetadata uint32_t nIndex = Params().GetConsensus().IsMain() ? BIP44_FIRO_INDEX : BIP44_TEST_INDEX; // Fail if not using HD wallet (no keypaths) if (hdChain.masterKeyID.IsNull()) throw std::runtime_error(std::string(__func__) + ": Non-HD wallet detected"); // use BIP44 keypath: m / purpose' / coin_type' / account' / change / address_index CKey key; //master key seed (256bit) CExtKey masterKey; //hd master key CExtKey purposeKey; //key at m/44' CExtKey coinTypeKey; //key at m/44'/<1/136>' (Testnet or Firo Coin Type respectively, according to SLIP-0044) CExtKey accountKey; //key at m/44'/<1/136>'/0' CExtKey externalChainChildKey; //key at m/44'/<1/136>'/0'/<c> (Standard: 0/1, Mints: 2) CExtKey childKey; //key at m/44'/<1/136>'/0'/<c>/<n> if(hdChain.nVersion >= CHDChain::VERSION_WITH_BIP39){ MnemonicContainer mContainer = mnemonicContainer; DecryptMnemonicContainer(mContainer); SecureVector seed = mContainer.GetSeed(); masterKey.SetMaster(&seed[0], seed.size()); } else { // try to get the master key if (!GetKey(hdChain.masterKeyID, key)) throw std::runtime_error(std::string(__func__) + ": Master key not found"); masterKey.SetMaster(key.begin(), key.size()); } // derive m/44' // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) masterKey.Derive(purposeKey, BIP44_INDEX | BIP32_HARDENED_KEY_LIMIT); // derive m/44'/136' purposeKey.Derive(coinTypeKey, nIndex | BIP32_HARDENED_KEY_LIMIT); // derive m/44'/136'/0' coinTypeKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); // derive m/44'/136'/0'/<c> accountKey.Derive(externalChainChildKey, nChange); // derive m/44'/136'/0'/<c>/<n> externalChainChildKey.Derive(childKey, nChild); secret = childKey.key; CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); return pubkey; } CPubKey CWallet::GenerateNewKey(uint32_t nChange, bool fWriteChain) { AssertLockHeld(cs_wallet); // mapKeyMetadata bool fCompressed = 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); metadata.nChange = Component(nChange, false); uint32_t nIndex = Params().GetConsensus().IsMain() ? BIP44_FIRO_INDEX : BIP44_TEST_INDEX; // use HD key derivation if HD was enabled during wallet creation // TODO: change code to foloow bitcoin structure more closely if (IsHDEnabled()) { // use BIP44 keypath: m / purpose' / coin_type' / account' / change / address_index CKey key; //master key seed (256bit) CExtKey masterKey; //hd master key CExtKey purposeKey; //key at m/44' CExtKey coinTypeKey; //key at m/44'/<1/136>' (Testnet or Firo Coin Type respectively, according to SLIP-0044) CExtKey accountKey; //key at m/44'/<1/136>'/0' CExtKey externalChainChildKey; //key at m/44'/<1/136>'/0'/<c> (Standard: 0/1, Mints: 2) CExtKey childKey; //key at m/44'/<1/136>'/0'/<c>/<n> //For bip39 we use it's original way for generating keys to make it compatible with hardware and software wallets if(hdChain.nVersion >= CHDChain::VERSION_WITH_BIP39){ MnemonicContainer mContainer = mnemonicContainer; DecryptMnemonicContainer(mContainer); SecureVector seed = mContainer.GetSeed(); masterKey.SetMaster(&seed[0], seed.size()); } else { // try to get the master key if (!GetKey(hdChain.masterKeyID, key)) throw std::runtime_error(std::string(__func__) + ": Master key not found"); masterKey.SetMaster(key.begin(), key.size()); } // derive m/44' // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) masterKey.Derive(purposeKey, BIP44_INDEX | BIP32_HARDENED_KEY_LIMIT); // derive m/44'/136' purposeKey.Derive(coinTypeKey, nIndex | BIP32_HARDENED_KEY_LIMIT); // derive m/44'/136'/0' coinTypeKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT); // derive m/44'/136'/0'/<c> accountKey.Derive(externalChainChildKey, nChange); // derive child key at next index, skip keys already known to the wallet do { externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounters[nChange]); metadata.hdKeypath = "m/44'/" + std::to_string(nIndex) + "'/0'/" + std::to_string(nChange) + "/" + std::to_string(hdChain.nExternalChainCounters[nChange]); metadata.hdMasterKeyID = hdChain.masterKeyID; metadata.nChild = Component(hdChain.nExternalChainCounters[nChange], false); // increment childkey index hdChain.nExternalChainCounters[nChange]++; } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; // update the chain model in the database if(fWriteChain){ if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } /* bitcoin 0.14: if (IsHDEnabled()) { DeriveNewChildKey(metadata, secret); */ } else { secret.MakeNewKey(fCompressed); } // Compressed public keys were introduced in version 0.6.0 if (fCompressed) SetMinVersion(FEATURE_COMPRPUBKEY); CPubKey pubkey = secret.GetPubKey(); assert(secret.VerifyPubKey(pubkey)); mapKeyMetadata[pubkey.GetID()] = metadata; UpdateTimeFirstKey(nCreationTime); if (!AddKeyPubKey(secret, pubkey)) throw std::runtime_error(std::string(__func__) + ": AddKey failed"); return pubkey; } void CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret) { // for now we use a fixed keypath scheme of m/0'/0'/k CKey key; //master key seed (256bit) CExtKey masterKey; //hd master key CExtKey accountKey; //key at m/0' CExtKey externalChainChildKey; //key at m/0'/0' CExtKey childKey; //key at m/0'/0'/<n>' // try to get the master key if (!GetKey(hdChain.masterKeyID, key)) throw std::runtime_error(std::string(__func__) + ": Master key not found"); masterKey.SetMaster(key.begin(), key.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' accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT); // 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 externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT); metadata.hdKeypath = "m/0'/0'/" + std::to_string(hdChain.nExternalChainCounter) + "'"; metadata.hdMasterKeyID = hdChain.masterKeyID; // increment childkey index hdChain.nExternalChainCounter++; } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; // update the chain model in the database if (!CWalletDB(strWalletFile).WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); } bool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey) { AssertLockHeld(cs_wallet); // mapKeyMetadata if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey)) return false; // check if we need to remove from watch-only CScript script; script = GetScriptForDestination(pubkey.GetID()); if (HaveWatchOnly(script)) RemoveWatchOnly(script); script = GetScriptForRawPubKey(pubkey); if (HaveWatchOnly(script)) RemoveWatchOnly(script); if (!fFileBacked) return true; if (!IsCrypted()) { return CWalletDB(strWalletFile).WriteKey(pubkey, secret.GetPrivKey(), mapKeyMetadata[pubkey.GetID()]); } return true; } bool CWallet::AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret)) return false; if (!fFileBacked) return true; { LOCK(cs_wallet); if (pwalletdbEncryption) return pwalletdbEncryption->WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); else return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey, vchCryptedSecret, mapKeyMetadata[vchPubKey.GetID()]); } return false; } bool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta) { AssertLockHeld(cs_wallet); // mapKeyMetadata UpdateTimeFirstKey(meta.nCreateTime); mapKeyMetadata[keyID] = meta; return true; } bool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } void CWallet::UpdateTimeFirstKey(int64_t nCreateTime) { AssertLockHeld(cs_wallet); 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 CWallet::AddCScript(const CScript& redeemScript) { if (!CCryptoKeyStore::AddCScript(redeemScript)) return false; if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript); } bool CWallet::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 = CBitcoinAddress(CScriptID(redeemScript)).ToString(); LogPrintf("%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 CCryptoKeyStore::AddCScript(redeemScript); } bool CWallet::AddWatchOnly(const CScript& dest) { if (!CCryptoKeyStore::AddWatchOnly(dest)) return false; const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)]; UpdateTimeFirstKey(meta.nCreateTime); NotifyWatchonlyChanged(true); if (!fFileBacked) return true; return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta); } bool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime) { mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime; return AddWatchOnly(dest); } bool CWallet::RemoveWatchOnly(const CScript &dest) { AssertLockHeld(cs_wallet); if (!CCryptoKeyStore::RemoveWatchOnly(dest)) return false; if (!HaveWatchOnly()) NotifyWatchonlyChanged(false); if (fFileBacked) if (!CWalletDB(strWalletFile).EraseWatchOnly(dest)) return false; return true; } bool CWallet::LoadWatchOnly(const CScript &dest) { return CCryptoKeyStore::AddWatchOnly(dest); } bool CWallet::Unlock(const SecureString &strWalletPassphrase, const bool& fFirstUnlock) { CCrypter crypter; CKeyingMaterial vMasterKey; { LOCK(cs_wallet); BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) continue; // try another master key if (CCryptoKeyStore::Unlock(vMasterKey, fFirstUnlock)) return true; } } return false; } bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase) { bool fWasLocked = IsLocked(); { LOCK(cs_wallet); Lock(); CCrypter crypter; CKeyingMaterial vMasterKey; BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys) { if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey)) return false; if (CCryptoKeyStore::Unlock(vMasterKey)) { int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime))); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod); pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; if (pMasterKey.second.nDeriveIterations < 25000) pMasterKey.second.nDeriveIterations = 25000; LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\n", pMasterKey.second.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod)) return false; if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey)) return false; CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second); if (fWasLocked) Lock(); return true; } } } return false; } void CWallet::SetBestChain(const CBlockLocator& loc) { CWalletDB walletdb(strWalletFile); walletdb.WriteBestBlock(loc); } bool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit) { LOCK(cs_wallet); // nWalletVersion if (nWalletVersion >= nVersion) return true; // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way if (fExplicit && nVersion > nWalletMaxVersion) nVersion = FEATURE_LATEST; nWalletVersion = nVersion; if (nVersion > nWalletMaxVersion) nWalletMaxVersion = nVersion; if (fFileBacked) { CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile); if (nWalletVersion > 40000) pwalletdb->WriteMinVersion(nWalletVersion); if (!pwalletdbIn) delete pwalletdb; } return true; } bool CWallet::SetMaxVersion(int nVersion) { LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion // cannot downgrade below current version if (nWalletVersion > nVersion) return false; nWalletMaxVersion = nVersion; return true; } std::set<uint256> CWallet::GetConflicts(const uint256& txid) const { std::set<uint256> result; AssertLockHeld(cs_wallet); std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid); if (it == mapWallet.end()) return result; const CWalletTx& wtx = it->second; std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapTxSpends.count(txin.prevout) <= 1) continue; // No conflict if zero or one spends range = mapTxSpends.equal_range(txin.prevout); for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it) result.insert(_it->second); } return result; } bool CWallet::HasWalletSpend(const uint256& txid) const { AssertLockHeld(cs_wallet); auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0)); return (iter != mapTxSpends.end() && iter->first.hash == txid); } void CWallet::Flush(bool shutdown) { bitdb.Flush(shutdown); } bool CWallet::Verify() { if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) return true; LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(0, 0, 0)); std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT); LogPrintf("Using wallet %s\n", walletFile); uiInterface.InitMessage(_("Verifying wallet...")); // Wallet file must be a plain filename without a directory if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile)) return InitError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string())); if (!bitdb.Open(GetDataDir())) { // try moving the database env out of the way boost::filesystem::path pathDatabase = GetDataDir() / "database"; boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime()); try { boost::filesystem::rename(pathDatabase, pathDatabaseBak); LogPrintf("Moved old %s to %s. Retrying.\n", pathDatabase.string(), pathDatabaseBak.string()); } catch (const boost::filesystem::filesystem_error&) { // failure is ok (well, not really, but it's not worse than what we started with) } // try again if (!bitdb.Open(GetDataDir())) { // if it still fails, it probably means we can't even create the database env return InitError(strprintf(_("Error initializing wallet database environment %s!"), GetDataDir())); } } if (GetBoolArg("-salvagewallet", false)) { // Recover readable keypairs: if (!CWalletDB::Recover(bitdb, walletFile, true)) return false; } if (boost::filesystem::exists(GetDataDir() / walletFile)) { CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover); if (r == CDBEnv::RECOVER_OK) { InitWarning(strprintf(_("Warning: Wallet file corrupt, data salvaged!" " Original %s saved as %s in %s; if" " your balance or transactions are incorrect you should" " restore from a backup."), walletFile, "wallet.{timestamp}.bak", GetDataDir())); } if (r == CDBEnv::RECOVER_FAIL) return InitError(strprintf(_("%s corrupt, salvage failed"), walletFile)); } return true; } void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range) { // We want all the wallet transactions in range to have the same metadata as // the oldest (smallest nOrderPos). // So: find smallest nOrderPos: int nMinOrderPos = std::numeric_limits<int>::max(); const CWalletTx* copyFrom = NULL; for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; int n = mapWallet[hash].nOrderPos; if (n < nMinOrderPos) { nMinOrderPos = n; copyFrom = &mapWallet[hash]; } } // Now copy data from copyFrom to rest: for (TxSpends::iterator it = range.first; it != range.second; ++it) { const uint256& hash = it->second; CWalletTx* copyTo = &mapWallet[hash]; if (copyFrom == copyTo) continue; if (!copyFrom->IsEquivalentTo(*copyTo)) continue; copyTo->mapValue = copyFrom->mapValue; copyTo->vOrderForm = copyFrom->vOrderForm; // fTimeReceivedIsTxTime not copied on purpose // nTimeReceived not copied on purpose copyTo->nTimeSmart = copyFrom->nTimeSmart; copyTo->fFromMe = copyFrom->fFromMe; copyTo->strFromAccount = copyFrom->strFromAccount; // nOrderPos not copied on purpose // cached members not copied on purpose } } /** * Outpoint is spent if any non-conflicted transaction * spends it: */ bool CWallet::IsSpent(const uint256 &hash, unsigned int n) const { auto tx = GetWalletTx(hash); // Try to handle mint output first. if (tx && tx->tx->vout.size() > n) { LOCK(cs_wallet); auto& script = tx->tx->vout[n].scriptPubKey; CWalletDB db(strWalletFile); if (script.IsZerocoinMint()) { return true; } else if (zwallet && script.IsSigmaMint()) { auto pub = sigma::ParseSigmaMintScript(script); uint256 hashPubcoin = primitives::GetPubCoinValueHash(pub); CMintMeta meta; if(!zwallet->GetTracker().GetMetaFromPubcoin(hashPubcoin, meta)){ return false; } return meta.isUsed; } else if (zwallet && (script.IsLelantusMint() || script.IsLelantusJMint())) { secp_primitives::GroupElement pubcoin; try { lelantus::ParseLelantusMintScript(script, pubcoin); } catch (std::invalid_argument &) { return false; } uint256 hashPubcoin = primitives::GetPubCoinValueHash(pubcoin); CLelantusMintMeta meta; if(!zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, meta)){ return false; } return meta.isUsed; } } // Normal output. const COutPoint outpoint(hash, n); std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range; range = mapTxSpends.equal_range(outpoint); for (TxSpends::const_iterator it = range.first; it != range.second; ++it) { const uint256& wtxid = it->second; std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid); if (mit != mapWallet.end()) { int depth = mit->second.GetDepthInMainChain(); if (depth > 0 || (depth == 0 && !mit->second.isAbandoned())) return true; // Spent } } return false; } void CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid) { mapTxSpends.insert(std::make_pair(outpoint, wtxid)); setWalletUTXO.erase(outpoint); std::pair<TxSpends::iterator, TxSpends::iterator> range; range = mapTxSpends.equal_range(outpoint); SyncMetaData(range); } void CWallet::AddToSpends(const uint256& wtxid) { assert(mapWallet.count(wtxid)); CWalletTx& thisTx = mapWallet[wtxid]; if (thisTx.IsCoinBase()) // Coinbases don't spend anything! return; BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) { if (!txin.IsZerocoinSpend() && !txin.IsSigmaSpend() && !txin.IsLelantusJoinSplit()) { AddToSpends(txin.prevout, wtxid); } } } bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) { if (IsCrypted()) return false; CKeyingMaterial vMasterKey; vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); CMasterKey kMasterKey; kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE); CCrypter crypter; int64_t nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime)); nStartTime = GetTimeMillis(); crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod); kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2; if (kMasterKey.nDeriveIterations < 25000) kMasterKey.nDeriveIterations = 25000; LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\n", kMasterKey.nDeriveIterations); if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod)) return false; if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey)) return false; { LOCK(cs_wallet); mapMasterKeys[++nMasterKeyMaxID] = kMasterKey; if (fFileBacked) { assert(!pwalletdbEncryption); pwalletdbEncryption = new CWalletDB(strWalletFile); if (!pwalletdbEncryption->TxnBegin()) { delete pwalletdbEncryption; pwalletdbEncryption = NULL; return false; } pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey); } if (!EncryptKeys(vMasterKey)) { if (fFileBacked) { pwalletdbEncryption->TxnAbort(); delete pwalletdbEncryption; } // We now probably have half of our keys encrypted in memory, and half not... // die and let the user reload the unencrypted wallet. assert(false); } // Encryption was introduced in version 0.4.0 SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true); if (fFileBacked) { if (!pwalletdbEncryption->TxnCommit()) { delete pwalletdbEncryption; // We now have keys encrypted in memory, but not on disk... // die to avoid confusion and let the user reload the unencrypted wallet. assert(false); } delete pwalletdbEncryption; pwalletdbEncryption = NULL; } Lock(); Unlock(strWalletPassphrase, true); if(!mnemonicContainer.IsNull() && hdChain.nVersion >= CHDChain::VERSION_WITH_BIP39) { assert(EncryptMnemonicContainer(vMasterKey)); SetMinVersion(FEATURE_HD); assert(SetMnemonicContainer(mnemonicContainer, false)); TopUpKeyPool(); } Lock(); // Need to completely rewrite the wallet file; if we don't, bdb might keep // bits of the unencrypted private key in slack space in the database file. CDB::Rewrite(strWalletFile); } NotifyStatusChanged(this); return true; } DBErrors CWallet::ReorderTransactions() { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile); // Old wallets didn't have any defined order for transactions // Probably a bad idea to change the output of this // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap. typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair; typedef std::multimap<int64_t, TxPair > TxItems; TxItems txByTime; for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { CWalletTx* wtx = &((*it).second); txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0))); } std::list<CAccountingEntry> acentries; walletdb.ListAccountCreditDebit("", acentries); BOOST_FOREACH(CAccountingEntry& entry, acentries) { txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry))); } nOrderPosNext = 0; std::vector<int64_t> nOrderPosOffsets; for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it) { CWalletTx *const pwtx = (*it).second.first; CAccountingEntry *const pacentry = (*it).second.second; int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos; if (nOrderPos == -1) { nOrderPos = nOrderPosNext++; nOrderPosOffsets.push_back(nOrderPos); if (pwtx) { if (!walletdb.WriteTx(*pwtx)) return DB_LOAD_FAIL; } else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DB_LOAD_FAIL; } else { int64_t nOrderPosOff = 0; BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets) { if (nOrderPos >= nOffsetStart) ++nOrderPosOff; } nOrderPos += nOrderPosOff; nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1); if (!nOrderPosOff) continue; // Since we're changing the order, write it back if (pwtx) { if (!walletdb.WriteTx(*pwtx)) return DB_LOAD_FAIL; } else if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry)) return DB_LOAD_FAIL; } } walletdb.WriteOrderPosNext(nOrderPosNext); return DB_LOAD_OK; } int64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb) { AssertLockHeld(cs_wallet); // nOrderPosNext int64_t nRet = nOrderPosNext++; if (pwalletdb) { pwalletdb->WriteOrderPosNext(nOrderPosNext); } else { CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext); } return nRet; } bool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment) { CWalletDB walletdb(strWalletFile); if (!walletdb.TxnBegin()) return false; int64_t nNow = GetAdjustedTime(); // Debit CAccountingEntry debit; debit.nOrderPos = IncOrderPosNext(&walletdb); debit.strAccount = strFrom; debit.nCreditDebit = -nAmount; debit.nTime = nNow; debit.strOtherAccount = strTo; debit.strComment = strComment; AddAccountingEntry(debit, &walletdb); // Credit CAccountingEntry credit; credit.nOrderPos = IncOrderPosNext(&walletdb); credit.strAccount = strTo; credit.nCreditDebit = nAmount; credit.nTime = nNow; credit.strOtherAccount = strFrom; credit.strComment = strComment; AddAccountingEntry(credit, &walletdb); if (!walletdb.TxnCommit()) return false; return true; } bool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew) { CWalletDB walletdb(strWalletFile); CAccount account; walletdb.ReadAccount(strAccount, account); if (!bForceNew) { if (!account.vchPubKey.IsValid()) bForceNew = true; else { // Check if the current key has been used CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end() && account.vchPubKey.IsValid(); ++it) BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout) if (txout.scriptPubKey == scriptPubKey) { bForceNew = true; break; } } } // Generate a new key if (bForceNew) { if (!GetKeyFromPool(account.vchPubKey)) return false; SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); walletdb.WriteAccount(strAccount, account); } pubKey = account.vchPubKey; return true; } void CWallet::MarkDirty() { { LOCK(cs_wallet); BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) item.second.MarkDirty(); } } namespace { void HandleSecretAddresses(CWallet & wallet, bip47::CAccountReceiver const & receiver) { if (wallet.IsLocked()) { wallet.NotifyBip47KeysChanged(receiver.getAccountNum(), chainActive.Tip()); return; } bip47::utils::AddReceiverSecretAddresses(receiver, wallet); } } bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash) { LOCK(cs_wallet); auto mi = mapWallet.find(originalHash); // There is a bug if MarkReplaced is not called on an existing wallet transaction. assert(mi != mapWallet.end()); CWalletTx& wtx = (*mi).second; // Ensure for now that we're not overwriting data assert(wtx.mapValue.count("replaced_by_txid") == 0); wtx.mapValue["replaced_by_txid"] = newHash.ToString(); CWalletDB walletdb(strWalletFile, "r+"); bool success = true; if (!walletdb.WriteTx(wtx)) { LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString()); success = false; } NotifyTransactionChanged(this, originalHash, CT_UPDATED); return success; } bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) { LOCK(cs_wallet); CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose); uint256 hash = wtxIn.GetHash(); // Inserts only if not already there, returns tx inserted or tx found std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn)); CWalletTx& wtx = (*ret.first).second; wtx.BindWallet(this); bool fInsertedNew = ret.second; if (fInsertedNew) { wtx.nTimeReceived = GetAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(&walletdb); wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); auto mnList = deterministicMNManager->GetListAtChainTip(); for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) { if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) { setWalletUTXO.insert(COutPoint(hash, i)); if (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i))) { LockCoin(COutPoint(hash, i)); } } } wtx.nTimeSmart = wtx.nTimeReceived; if (!wtxIn.hashUnset()) { if (mapBlockIndex.count(wtxIn.hashBlock)) { int64_t latestNow = wtx.nTimeReceived; int64_t latestEntry = 0; { // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future int64_t latestTolerated = latestNow + 300; const TxItems & txOrdered = wtxOrdered; for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) { CWalletTx *const pwtx = (*it).second.first; if (pwtx == &wtx) continue; CAccountingEntry *const pacentry = (*it).second.second; int64_t nSmartTime; if (pwtx) { nSmartTime = pwtx->nTimeSmart; if (!nSmartTime) nSmartTime = pwtx->nTimeReceived; } else nSmartTime = pacentry->nTime; if (nSmartTime <= latestTolerated) { latestEntry = nSmartTime; if (nSmartTime > latestNow) latestNow = nSmartTime; break; } } } int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime(); wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow)); } else LogPrintf("AddToWallet(): found %s in block %s not in index\n", wtxIn.GetHash().ToString(), wtxIn.hashBlock.ToString()); } AddToSpends(hash); } bool fUpdated = false; if (!fInsertedNew) { // Merge if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } // If no longer abandoned, update if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned()) { wtx.hashBlock = wtxIn.hashBlock; fUpdated = true; } if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex)) { wtx.nIndex = wtxIn.nIndex; fUpdated = true; } if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) { wtx.fFromMe = wtxIn.fFromMe; fUpdated = true; } } //// debug print LogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); // Write to disk if (fInsertedNew || fUpdated) if (!walletdb.WriteTx(wtx)) return false; // Handle bip47 notification tx if (fInsertedNew) { HandleBip47Transaction(wtx); } // Break debit/credit balance caches: wtx.MarkDirty(); // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); // notify an external script when a wallet transaction comes in or is updated std::string strCmd = GetArg("-walletnotify", ""); if ( !strCmd.empty()) { boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); boost::thread t(runCommand, strCmd); // thread runs free } return true; } bool CWallet::LoadToWallet(const CWalletTx& wtxIn) { uint256 hash = wtxIn.GetHash(); mapWallet[hash] = wtxIn; CWalletTx& wtx = mapWallet[hash]; wtx.BindWallet(this); wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0))); AddToSpends(hash); BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) { CWalletTx& prevtx = mapWallet[txin.prevout.hash]; if (prevtx.nIndex == -1 && !prevtx.hashUnset()) { MarkConflicted(prevtx.hashBlock, wtx.GetHash()); } } } return true; } /** * Add a transaction to the wallet, or update it. pIndex and posInBlock should * be set when the transaction was known to be included in a block. When * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not * updated in AddToWallet, but notifications happen and cached balances are * marked dirty. * If fUpdate is true, existing transactions will be updated. * TODO: One exception to this is that the abandoned state is cleared under the * assumption that any further notification of a transaction that was considered * abandoned is an indication that it is not safe to be considered abandoned. * Abandoned state should probably be more carefuly tracked via different * posInBlock signals or by checking mempool presence when necessary. */ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) { { AssertLockHeld(cs_wallet); if (posInBlock != -1) { if(!(tx.IsCoinBase() || tx.IsSigmaSpend() || tx.IsZerocoinRemint() || tx.IsZerocoinSpend()) || tx.IsLelantusJoinSplit()) { BOOST_FOREACH(const CTxIn& txin, tx.vin) { std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout); while (range.first != range.second) { if (range.first->second != tx.GetHash()) { LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n); MarkConflicted(pIndex->GetBlockHash(), range.first->second); } range.first++; } } } } bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; if (fExisted || IsMine(tx) || IsFromMe(tx)) { /* Check if any keys in the wallet keypool that were supposed to be unused * have appeared in a new transaction. If so, remove those keys from the keypool. * This can happen when restoring an old wallet backup that does not contain * the mostly recently created transactions from newer versions of the wallet. */ // loop though all outputs for (const CTxOut& txout: tx.vout) { // extract addresses and check if they match with an unused keypool key std::vector<CKeyID> vAffected; CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey); for (const CKeyID &keyid : vAffected) { std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid); if (mi != m_pool_key_to_index.end()) { LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\n", __func__); MarkReserveKeysAsUsed(mi->second); if (!TopUpKeyPool()) { LogPrintf("%s: Topping up keypool failed (locked wallet)\n", __func__); } } } } CWalletTx wtx(this, MakeTransactionRef(tx)); // Get merkle branch if transaction was found in a block if (posInBlock != -1) wtx.SetMerkleBranch(pIndex, posInBlock); return AddToWallet(wtx, false); } } return false; } bool CWallet::AbandonTransaction(const uint256& hashTx) { LOCK2(cs_main, cs_wallet); CWalletDB walletdb(strWalletFile, "r+"); std::set<uint256> todo; std::set<uint256> done; // Can't mark abandoned if confirmed or in mempool assert(mapWallet.count(hashTx)); CWalletTx& origtx = mapWallet[hashTx]; if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool() || origtx.InStempool() || origtx.IsLockedByLLMQInstantSend()) { return false; } todo.insert(hashTx); while (!todo.empty()) { uint256 now = *todo.begin(); todo.erase(now); done.insert(now); assert(mapWallet.count(now)); CWalletTx& wtx = mapWallet[now]; int currentconfirm = wtx.GetDepthInMainChain(); // If the orig tx was not in block, none of its spends can be assert(currentconfirm <= 0); // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon} if (currentconfirm == 0 && !wtx.isAbandoned()) { // If the orig tx was not in block/mempool, none of its spends can be in mempool assert(!wtx.InMempool()); assert(!wtx.InStempool()); wtx.nIndex = -1; wtx.setAbandoned(); wtx.MarkDirty(); walletdb.WriteTx(wtx); NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED); // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0)); while (iter != mapTxSpends.end() && iter->first.hash == now) { if (!done.count(iter->second)) { todo.insert(iter->second); } iter++; } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); } } if (wtx.tx->IsSigmaSpend()) { // find out coin serial number assert(wtx.tx->vin.size() == 1); const CTxIn &txin = wtx.tx->vin[0]; // NOTE(martun): +1 on the next line stands for 1 byte in which the opcode of // OP_SIGMASPEND is written. // because the size of serialized spend is also written, probably in 3 bytes. CDataStream serializedCoinSpend((const char *)&*(txin.scriptSig.begin() + 1), (const char *)&*txin.scriptSig.end(), SER_NETWORK, PROTOCOL_VERSION); sigma::CoinSpend spend(sigma::Params::get_default(), serializedCoinSpend); Scalar serial = spend.getCoinSerialNumber(); // mark corresponding mint as unspent uint256 hashSerial = primitives::GetSerialHash(serial); CMintMeta meta; if(zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta)){ meta.isUsed = false; zwallet->GetTracker().UpdateState(meta); // erase sigma spend entry CSigmaSpendEntry spendEntry; spendEntry.coinSerial = serial; walletdb.EraseCoinSpendSerialEntry(spendEntry); } } else if (wtx.tx->IsLelantusJoinSplit()) { // find out coin serial number assert(wtx.tx->vin.size() == 1); std::unique_ptr<lelantus::JoinSplit> joinsplit; try { joinsplit = lelantus::ParseLelantusJoinSplit(*wtx.tx); } catch (...) { continue; } const std::vector<Scalar>& serials = joinsplit->getCoinSerialNumbers(); for (const auto& serial : serials) { // mark corresponding mint as unspent uint256 hashSerial = primitives::GetSerialHash(serial); CLelantusMintMeta meta; if(zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta)){ meta.isUsed = false; zwallet->GetTracker().UpdateState(meta); // erase lelantus spend entry CLelantusSpendEntry spendEntry; spendEntry.coinSerial = serial; walletdb.EraseLelantusSpendSerialEntry(spendEntry); } } } if (wtx.tx->IsSigmaMint()) { for (const CTxOut &txout: wtx.tx->vout) { if (!txout.scriptPubKey.IsSigmaMint()) continue; try { auto groupElement = sigma::ParseSigmaMintScript(txout.scriptPubKey); uint256 hashPubcoin = primitives::GetPubCoinValueHash(groupElement); CMintMeta meta; if (zwallet->GetTracker().GetMetaFromPubcoin(hashPubcoin, meta)) zwallet->GetTracker().Archive(meta); } catch (std::invalid_argument &) { continue; } } } if (wtx.tx->IsLelantusMint()) { for (const CTxOut &txout: wtx.tx->vout) { if (!txout.scriptPubKey.IsLelantusMint() && !txout.scriptPubKey.IsLelantusJMint()) continue; try { secp_primitives::GroupElement groupElement; lelantus::ParseLelantusMintScript(txout.scriptPubKey, groupElement); uint256 hashPubcoin = primitives::GetPubCoinValueHash(groupElement); CLelantusMintMeta meta; if (zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, meta)) zwallet->GetTracker().Archive(meta); } catch (std::invalid_argument &) { continue; } } } } return true; } void CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx) { LOCK2(cs_main, cs_wallet); int conflictconfirms = 0; if (mapBlockIndex.count(hashBlock)) { CBlockIndex* pindex = mapBlockIndex[hashBlock]; if (chainActive.Contains(pindex)) { conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1); } } // If number of conflict confirms cannot be determined, this means // that the block is still unknown or not yet part of the main chain, // for example when loading the wallet during a reindex. Do nothing in that // case. if (conflictconfirms >= 0) return; // Do not flush the wallet here for performance reasons CWalletDB walletdb(strWalletFile, "r+", false); std::set<uint256> todo; std::set<uint256> done; todo.insert(hashTx); while (!todo.empty()) { uint256 now = *todo.begin(); todo.erase(now); done.insert(now); assert(mapWallet.count(now)); CWalletTx& wtx = mapWallet[now]; int currentconfirm = wtx.GetDepthInMainChain(); if (conflictconfirms < currentconfirm) { // Block is 'more conflicted' than current confirm; update. // Mark transaction as conflicted with this block. wtx.nIndex = -1; wtx.hashBlock = hashBlock; wtx.MarkDirty(); walletdb.WriteTx(wtx); // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0)); while (iter != mapTxSpends.end() && iter->first.hash == now) { if (!done.count(iter->second)) { todo.insert(iter->second); } iter++; } // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be recomputed BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); } } } } void CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock) { LOCK2(cs_main, cs_wallet); if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true)) return; // Not one of ours // If a transaction changes 'conflicted' state, that changes the balance // available of the outputs it spends. So force those to be // recomputed, also: BOOST_FOREACH(const CTxIn& txin, tx.vin) { if (mapWallet.count(txin.prevout.hash)) mapWallet[txin.prevout.hash].MarkDirty(); } } isminetype CWallet::IsMine(const CTxIn &txin, const CTransaction& tx) const { LOCK(cs_wallet); if (txin.IsZerocoinSpend()) { return ISMINE_NO; } else if (txin.IsSigmaSpend()) { CWalletDB db(strWalletFile); CDataStream serializedCoinSpend( std::vector<char>(txin.scriptSig.begin() + 1, txin.scriptSig.end()), SER_NETWORK, PROTOCOL_VERSION); sigma::Params* sigmaParams = sigma::Params::get_default(); sigma::CoinSpend spend(sigmaParams, serializedCoinSpend); if (db.HasCoinSpendSerialEntry(spend.getCoinSerialNumber())) { return ISMINE_SPENDABLE; } } else if (txin.IsLelantusJoinSplit()) { CWalletDB db(strWalletFile); std::unique_ptr<lelantus::JoinSplit> joinsplit; try { joinsplit = lelantus::ParseLelantusJoinSplit(tx); } catch (...) { return ISMINE_NO; } if (db.HasLelantusSpendSerialEntry(joinsplit->getCoinSerialNumbers()[0]) || db.HasCoinSpendSerialEntry(joinsplit->getCoinSerialNumbers()[0])) { return ISMINE_SPENDABLE; } } else if (txin.IsZerocoinRemint()) { return ISMINE_NO; } else { std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.tx->vout.size()) return IsMine(prev.tx->vout[txin.prevout.n]); } } return ISMINE_NO; } // Note that this function doesn't distinguish between a 0-valued input, // and a not-"is mine" (according to the filter) input. CAmount CWallet::GetDebit(const CTxIn &txin, const CTransaction& tx, const isminefilter& filter) const { LOCK(cs_wallet); if (txin.IsZerocoinSpend()) { // Reverting it to its pre-Sigma state. goto end; } else if (txin.IsSigmaSpend()) { if (!(filter & ISMINE_SPENDABLE)) { goto end; } CWalletDB db(strWalletFile); std::unique_ptr<sigma::CoinSpend> spend; try { std::tie(spend, std::ignore) = sigma::ParseSigmaSpend(txin); } catch (CBadTxIn&) { goto end; } if (db.HasCoinSpendSerialEntry(spend->getCoinSerialNumber())) { return spend->getIntDenomination(); } } else if (txin.IsZerocoinRemint()) { return 0; } else if (txin.IsLelantusJoinSplit()) { if (!(filter & ISMINE_SPENDABLE)) { goto end; } CWalletDB db(strWalletFile); std::unique_ptr<lelantus::JoinSplit> joinsplit; try { joinsplit = lelantus::ParseLelantusJoinSplit(tx); } catch (...) { goto end; } CAmount amount = 0; const std::vector<Scalar>& serials = joinsplit->getCoinSerialNumbers(); for (const auto& serial : serials) { CLelantusSpendEntry lelantusSpend; if(db.ReadLelantusSpendSerialEntry(serial, lelantusSpend)) amount += lelantusSpend.amount; } return amount; } else { std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); if (mi != mapWallet.end()) { const CWalletTx& prev = (*mi).second; if (txin.prevout.n < prev.tx->vout.size()) if (IsMine(prev.tx->vout[txin.prevout.n]) & filter) return prev.tx->vout[txin.prevout.n].nValue; } } end: return 0; } isminetype CWallet::IsMine(const CTxOut &txout) const { LOCK(cs_wallet); if (txout.scriptPubKey.IsSigmaMint() || txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) { CWalletDB db(strWalletFile); secp_primitives::GroupElement pub; if (txout.scriptPubKey.IsSigmaMint()) { try { pub = sigma::ParseSigmaMintScript(txout.scriptPubKey); } catch (std::invalid_argument &) { return ISMINE_NO; } } else { try { lelantus::ParseLelantusMintScript(txout.scriptPubKey, pub); } catch (std::invalid_argument &) { return ISMINE_NO; } } return db.HasHDMint(pub) ? ISMINE_SPENDABLE : ISMINE_NO; } else { return ::IsMine(*this, txout.scriptPubKey); } } CAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error(std::string(__func__) + ": value out of range"); if (txout.scriptPubKey.IsLelantusJMint()) { if (!(filter & ISMINE_SPENDABLE)) return 0; CWalletDB db(strWalletFile); secp_primitives::GroupElement pub; try { std::vector<unsigned char> encryptedValue; lelantus::ParseLelantusJMintScript(txout.scriptPubKey, pub, encryptedValue); } catch (std::invalid_argument&) { return ISMINE_NO; } uint256 hashPubcoin = primitives::GetPubCoinValueHash(pub); CHDMint dMint; if (db.ReadHDMint(hashPubcoin, true, dMint)) { return dMint.GetAmount(); } return 0; } return ((IsMine(txout) & filter) ? txout.nValue : 0); } bool CWallet::IsChange(const uint256& tx, const CTxOut &txout) const { auto wtx = GetWalletTx(tx); if (!wtx) { throw std::invalid_argument("The specified transaction hash is not belong to the wallet"); } return wtx->IsChange(txout); } CAmount CWallet::GetChange(const uint256& tx, const CTxOut &txout) const { if (!MoneyRange(txout.nValue)) throw std::runtime_error(std::string(__func__) + ": value out of range"); return (IsChange(tx, txout) ? txout.nValue : 0); } bool CWallet::IsMine(const CTransaction& tx) const { BOOST_FOREACH(const CTxOut& txout, tx.vout) if (IsMine(txout)) return true; return false; } bool CWallet::IsFromMe(const CTransaction& tx) const { return (GetDebit(tx, ISMINE_ALL) > 0); } CAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const { CAmount nDebit = 0; BOOST_FOREACH(const CTxIn& txin, tx.vin) { nDebit += GetDebit(txin, tx, filter); if (!MoneyRange(nDebit)) throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nDebit; } bool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const { LOCK(cs_wallet); BOOST_FOREACH(const CTxIn& txin, tx.vin) { auto mi = mapWallet.find(txin.prevout.hash); if (mi == mapWallet.end()) return false; // any unknown inputs can't be from us const CWalletTx& prev = (*mi).second; if (txin.prevout.n >= prev.tx->vout.size()) return false; // invalid input! if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter)) return false; } return true; } CAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const { CAmount nCredit = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nCredit += GetCredit(txout, filter); if (!MoneyRange(nCredit)) throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nCredit; } CAmount CWallet::GetChange(const CTransaction& tx) const { CAmount nChange = 0; BOOST_FOREACH(const CTxOut& txout, tx.vout) { nChange += GetChange(tx.GetHash(), txout); if (!MoneyRange(nChange)) throw std::runtime_error(std::string(__func__) + ": value out of range"); } return nChange; } CPubKey CWallet::GenerateNewHDMasterKey() { CKey key; key.MakeNewKey(true); int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); // calculate the pubkey CPubKey pubkey = key.GetPubKey(); assert(key.VerifyPubKey(pubkey)); // set the hd keypath to "m" -> Master, refers the masterkeyid to itself metadata.hdKeypath = "m"; metadata.hdMasterKeyID = pubkey.GetID(); { LOCK(cs_wallet); // mem store the metadata mapKeyMetadata[pubkey.GetID()] = metadata; // write the key&metadata to the database if (!AddKeyPubKey(key, pubkey)) throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); } return pubkey; } void CWallet::GenerateNewMnemonic() { CHDChain newHdChain; MnemonicContainer mnContainer; std::string strSeed = GetArg("-hdseed", "not hex"); bool isHDSeedSet = strSeed != "not hex"; if(isHDSeedSet && IsHex(strSeed)) { std::vector<unsigned char> seed = ParseHex(strSeed); if (!mnContainer.SetSeed(SecureVector(seed.begin(), seed.end()))) throw std::runtime_error(std::string(__func__) + ": SetSeed failed"); newHdChain.masterKeyID = CKeyID(Hash160(seed.begin(), seed.end())); } else { LogPrintf("CWallet::GenerateNewMnemonic -- Generating new MnemonicContainer\n"); std::string mnemonic = GetArg("-mnemonic", ""); std::string mnemonicPassphrase = GetArg("-mnemonicpassphrase", ""); //remove trailing string identifiers boost::algorithm::trim_if(mnemonic, [](char c){return c=='\"' || c=='\'';}); boost::algorithm::trim_if(mnemonicPassphrase, [](char c){return c=='\"' || c=='\'';}); //Use 24 words by default; bool use12Words = GetBoolArg("-use12", false); mnContainer.Set12Words(use12Words); SecureString secureMnemonic(mnemonic.begin(), mnemonic.end()); SecureString securePassphrase(mnemonicPassphrase.begin(), mnemonicPassphrase.end()); if (!mnContainer.SetMnemonic(secureMnemonic, securePassphrase)) throw std::runtime_error(std::string(__func__) + ": SetMnemonic failed"); newHdChain.masterKeyID = CKeyID(Hash160(mnContainer.seed.begin(), mnContainer.seed.end())); } if (!SetHDChain(newHdChain, false)) throw std::runtime_error(std::string(__func__) + ": SetHDChain failed"); if (!SetMnemonicContainer(mnContainer, false)) throw std::runtime_error(std::string(__func__) + ": SetMnemonicContainer failed"); } bool CWallet::SetHDMasterKey(const CPubKey &pubkey, const int cHDChainVersion) { LOCK(cs_wallet); // ensure this wallet.dat can only be opened by clients supporting HD SetMinVersion(FEATURE_HD); // store the keyid (hash160) together with // the child index counter in the database // as a hdchain object CHDChain newHdChain; newHdChain.nVersion = cHDChainVersion; newHdChain.masterKeyID = pubkey.GetID(); SetHDChain(newHdChain, false); return true; } bool CWallet::SetHDChain(const CHDChain &chain, bool memonly, bool& upgradeChain, bool genNewKeyPool) { LOCK(cs_wallet); upgradeChain = (chain.nVersion==CHDChain::VERSION_BASIC); if (upgradeChain && !IsLocked()) { // Upgrade HDChain to latest version CHDChain newChain; newChain.masterKeyID = chain.masterKeyID; newChain.nVersion = CHDChain::VERSION_WITH_BIP44; // old versions cannot use mnemonic // whether to generate the keypool now (conditional as leads to DB deadlock if loading DB simultaneously) if (genNewKeyPool) NewKeyPool(); if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(newChain)) throw std::runtime_error(std::string(__func__) + ": writing chain failed"); hdChain = newChain; } else { if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain)) throw std::runtime_error(std::string(__func__) + ": writing chain failed"); hdChain = chain; } return true; } bool CWallet::IsHDEnabled() { return !hdChain.masterKeyID.IsNull(); } bool CWallet::SetMnemonicContainer(const MnemonicContainer& mnContainer, bool memonly) { if (!memonly && !CWalletDB(strWalletFile).WriteMnemonic(mnContainer)) throw std::runtime_error(std::string(__func__) + ": writing chain failed"); mnemonicContainer = mnContainer; return true; } bool CWallet::EncryptMnemonicContainer(const CKeyingMaterial& vMasterKeyIn) { if (!IsCrypted()) return false; if (mnemonicContainer.IsCrypted()) return true; uint256 id = uint256S(hdChain.masterKeyID.GetHex()); std::vector<unsigned char> cryptedSeed; if (!EncryptMnemonicSecret(vMasterKeyIn, mnemonicContainer.GetSeed(), id, cryptedSeed)) return false; SecureVector secureCryptedSeed(cryptedSeed.begin(), cryptedSeed.end()); if (!mnemonicContainer.SetSeed(secureCryptedSeed)) return false; SecureString mnemonic; if (mnemonicContainer.GetMnemonic(mnemonic)) { std::vector<unsigned char> cryptedMnemonic; SecureVector vectorMnemonic(mnemonic.begin(), mnemonic.end()); if ((!mnemonic.empty() && !EncryptMnemonicSecret(vMasterKeyIn, vectorMnemonic, id, cryptedMnemonic))) return false; SecureVector secureCryptedMnemonic(cryptedMnemonic.begin(), cryptedMnemonic.end()); if (!mnemonicContainer.SetMnemonic(secureCryptedMnemonic)) return false; } mnemonicContainer.SetCrypted(true); return true; } bool CWallet::DecryptMnemonicContainer(MnemonicContainer& mnContainer) { if (!IsCrypted()) return true; if (!mnemonicContainer.IsCrypted()) return false; uint256 id = uint256S(hdChain.masterKeyID.GetHex()); SecureVector seed; SecureVector cryptedSeed = mnemonicContainer.GetSeed(); std::vector<unsigned char> vCryptedSeed(cryptedSeed.begin(), cryptedSeed.end()); if (!DecryptMnemonicSecret(vCryptedSeed, id, seed)) return false; mnContainer = mnemonicContainer; if (!mnContainer.SetSeed(seed)) return false; SecureString cryptedMnemonic; if (mnemonicContainer.GetMnemonic(cryptedMnemonic)) { SecureVector vectorMnemonic; std::vector<unsigned char> CryptedMnemonic(cryptedMnemonic.begin(), cryptedMnemonic.end()); if (!CryptedMnemonic.empty() && !DecryptMnemonicSecret(CryptedMnemonic, id, vectorMnemonic)) return false; if (!mnContainer.SetMnemonic(vectorMnemonic)) return false; } mnContainer.SetCrypted(false); return true; } int64_t CWalletTx::GetTxTime() const { int64_t n = nTimeSmart; return n ? n : nTimeReceived; } int CWalletTx::GetRequestCount() const { // Returns -1 if it wasn't being tracked int nRequests = -1; { LOCK(pwallet->cs_wallet); if (IsCoinBase()) { // Generated block if (!hashUnset()) { std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock); if (mi != pwallet->mapRequestCount.end()) nRequests = (*mi).second; } } else { // Did anyone request this transaction? std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash()); if (mi != pwallet->mapRequestCount.end()) { nRequests = (*mi).second; // How about the block it's in? if (nRequests == 0 && !hashUnset()) { std::map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock); if (_mi != pwallet->mapRequestCount.end()) nRequests = (*_mi).second; else nRequests = 1; // If it's in someone else's block it must have got out } } } } return nRequests; } void CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived, std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const { nFee = 0; listReceived.clear(); listSent.clear(); strSentAccount = strFromAccount; // Compute fee: CAmount nDebit = GetDebit(filter); if (nDebit > 0) // debit>0 means we signed/sent this transaction { if (!tx->IsLelantusJoinSplit()) { CAmount nValueOut = tx->GetValueOut(); nFee = nDebit - nValueOut; } else try { nFee = lelantus::ParseLelantusJoinSplit(*tx)->getFee(); } catch (...) { // do nothing } } // Sent/received. for (unsigned int i = 0; i < tx->vout.size(); ++i) { const CTxOut& txout = tx->vout[i]; isminetype fIsMine = pwallet->IsMine(txout); // Only need to handle txouts if AT LEAST one of these is true: // 1) they debit from us (sent) // 2) the output is to us (received) if (nDebit > 0) { // Don't report 'change' txouts if (IsChange(static_cast<uint32_t>(i))) continue; // GetHash().ToString(), (nEmbargo - nCurrTime) / 1000000); CInv inv(MSG_DANDELION_TX, GetHash()); return CNode::localDandelionDestinationPushInventory(inv); } else { // LogPrintf("Relaying wtx %s\n", GetHash().ToString()); g_connman->RelayTransaction(*this); return true; } } } LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\n", this->GetHash().ToString()); address = CNoDestination(); } CAmount nValue; if(txout.scriptPubKey.IsLelantusJMint()) { LOCK(pwalletMain->cs_wallet); nValue = pwallet->GetCredit(txout, ISMINE_SPENDABLE); } else { nValue = txout.nValue; } COutputEntry output = {address, nValue, (int)i}; // If we are debited by the transaction, add the output as a "sent" entry if (nDebit > 0) listSent.push_back(output); // If we are receiving the output, add it as a "received" entry if (fIsMine & filter) listReceived.push_back(output); } } /** * Scan the block chain (starting in pindexStart) for transactions * from or to us. If fUpdate is true, found transactions that already * exist in the wallet will be updated. * * Returns pointer to the first block in the last contiguous range that was * successfully scanned. * */ CBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex *pindexStart, bool fUpdate, bool fRecoverMnemonic) { CBlockIndex* ret = nullptr; int64_t nNow = GetTime(); const CChainParams& chainParams = Params(); CBlockIndex* pindex = pindexStart; { LOCK2(cs_main, cs_wallet); // no need to read and scan block, if block was created before // our wallet birthday (as adjusted for block time variability) // if you are recovering wallet with mnemonics start rescan from block when mnemonics implemented in Firo if (fRecoverMnemonic) { pindex = chainActive[chainParams.GetConsensus().nMnemonicBlock]; if (pindex == NULL) pindex = chainActive.Tip(); } else while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200))) pindex = chainActive.Next(pindex); ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex); double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip()); while (pindex) { // A temporary fix for inability to Ctrl-C rescan when restoring a wallet (will be fixed in 0.15.) if (ShutdownRequested()) return nullptr; if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0) ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100)))); if (GetTime() >= nNow + 60) { nNow = GetTime(); LogPrintf("Still rescanning. At block %d. Progress=%f\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex)); } CBlock block; if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) { AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate); } if (!ret) { ret = pindex; } } else { ret = nullptr; } pindex = chainActive.Next(pindex); } ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI } return ret; } void CWallet::ReacceptWalletTransactions() { // If transactions aren't being broadcasted, don't let them into local mempool either if (!fBroadcastTransactions) return; LOCK2(cs_main, cs_wallet); std::map<int64_t, CWalletTx*> mapSorted; // Sort pending wallet transactions based on their initial wallet insertion order BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { const uint256& wtxid = item.first; CWalletTx& wtx = item.second; assert(wtx.GetHash() == wtxid); int nDepth = wtx.GetDepthInMainChain(); if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned() && !wtx.IsLockedByLLMQInstantSend())) { mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx)); } } // Try to add wallet transactions to memory pool BOOST_FOREACH(PAIRTYPE(const int64_t, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *(item.second); CValidationState state; // the app was closed and re-opened, do NOT check their // serial numbers, and DO NOT try to mark their serial numbers // a second time. We assume those operations were already done. wtx.AcceptToMemoryPool(maxTxFee, state); // If Dandelion enabled, relay transaction once again. if (GetBoolArg("-dandelion", true)) { wtx.RelayWalletTransaction(g_connman.get()); } } } bool CWalletTx::RelayWalletTransaction(CConnman* connman) { assert(pwallet->GetBroadcastTransactions()); if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0) { CValidationState state; /* GetDepthInMainChain already catches known conflicts. */ if (InMempool() || InStempool() || AcceptToMemoryPool(maxTxFee, state)) { // If Dandelion enabled, push inventory item to just one destination. if (GetBoolArg("-dandelion", true)) { int64_t nCurrTime = GetTimeMicros(); int64_t nEmbargo = 1000000 * DANDELION_EMBARGO_MINIMUM + PoissonNextSend(nCurrTime, DANDELION_EMBARGO_AVG_ADD); CNode::insertDandelionEmbargo(GetHash(), nEmbargo); CInv inv(MSG_DANDELION_TX, GetHash()); return CNode::localDandelionDestinationPushInventory(inv); } else { // LogPrintf("Relaying wtx %s\n", GetHash().ToString()); if (connman) { connman->RelayTransaction(*this); return true; } } } } return false; } std::set<uint256> CWalletTx::GetConflicts() const { std::set<uint256> result; if (pwallet != NULL) { uint256 myHash = GetHash(); result = pwallet->GetConflicts(myHash); result.erase(myHash); } return result; } CAmount CWalletTx::GetDebit(const isminefilter& filter) const { if (tx->vin.empty()) return 0; CAmount debit = 0; if(filter & ISMINE_SPENDABLE) { if (fDebitCached) debit += nDebitCached; else { nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE); fDebitCached = true; debit += nDebitCached; } } if(filter & ISMINE_WATCH_ONLY) { if(fWatchDebitCached) debit += nWatchDebitCached; else { nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY); fWatchDebitCached = true; debit += nWatchDebitCached; } } return debit; } CAmount CWalletTx::GetCredit(const isminefilter& filter) const { // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; CAmount credit = 0; if (filter & ISMINE_SPENDABLE) { // GetBalance can assume transactions in mapWallet won't change if (fCreditCached) credit += nCreditCached; else { nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); fCreditCached = true; credit += nCreditCached; } } if (filter & ISMINE_WATCH_ONLY) { if (fWatchCreditCached) credit += nWatchCreditCached; else { nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); fWatchCreditCached = true; credit += nWatchCreditCached; } } return credit; } CAmount CWalletTx::GetImmatureCredit(bool fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureCreditCached) return nImmatureCreditCached; nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE); fImmatureCreditCached = true; return nImmatureCreditCached; } return 0; } CAmount CWalletTx::GetAvailableCredit(bool fUseCache, bool fExcludeLocked) const { if (pwallet == 0) return 0; // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; // We cannot use cache if vout contains mints due to it will not update when it spend if (fUseCache && fAvailableCreditCached && !tx->IsZerocoinMint() && !tx->IsSigmaMint() && !tx->IsLelantusMint() && !fExcludeLocked) return nAvailableCreditCached; CAmount nCredit = 0; uint256 hashTx = GetHash(); for (unsigned int i = 0; i < tx->vout.size(); i++) { if (!pwallet->IsSpent(hashTx, i)) { const CTxOut &txout = tx->vout[i]; bool isPrivate = txout.scriptPubKey.IsZerocoinMint() || txout.scriptPubKey.IsSigmaMint() || txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint(); bool condition = isPrivate; if (fExcludeLocked) condition = (isPrivate || pwallet->IsLockedCoin(hashTx, i)); nCredit += condition ? 0 : pwallet->GetCredit(txout, ISMINE_SPENDABLE); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } nAvailableCreditCached = nCredit; fAvailableCreditCached = true; if (fExcludeLocked) fAvailableCreditCached = false; return nCredit; } CAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const { if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain()) { if (fUseCache && fImmatureWatchCreditCached) return nImmatureWatchCreditCached; nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY); fImmatureWatchCreditCached = true; return nImmatureWatchCreditCached; } return 0; } CAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const { if (pwallet == 0) return 0; // Must wait until coinbase is safely deep enough in the chain before valuing it if (IsCoinBase() && GetBlocksToMaturity() > 0) return 0; if (fUseCache && fAvailableWatchCreditCached) return nAvailableWatchCreditCached; CAmount nCredit = 0; for (unsigned int i = 0; i < tx->vout.size(); i++) { if (!pwallet->IsSpent(GetHash(), i)) { const CTxOut &txout = tx->vout[i]; nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY); if (!MoneyRange(nCredit)) throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range"); } } nAvailableWatchCreditCached = nCredit; fAvailableWatchCreditCached = true; return nCredit; } CAmount CWalletTx::GetChange() const { if (fChangeCached) return nChangeCached; nChangeCached = pwallet->GetChange(*this); fChangeCached = true; return nChangeCached; } bool CWalletTx::InMempool() const { LOCK(mempool.cs); if (mempool.exists(GetHash())) { return true; } return false; } bool CWalletTx::InStempool() const { if (txpools.getStemTxPool().exists(GetHash())) { return true; } return false; } bool CWalletTx::IsTrusted() const { // Quick answer in most cases. // Zerocoin spend is always false due to it use nSequence incorrectly. if (!tx->IsZerocoinSpend() && !CheckFinalTx(*this)) return false; int nDepth = GetDepthInMainChain(); if (nDepth >= 1) return true; if (nDepth < 0) return false; if (IsLockedByLLMQInstantSend()) return true; if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx's cached debit return false; // Don't trust unconfirmed transactions from us unless they are in the mempool. if (!InMempool() && !InStempool()) return false; // Trusted if all inputs are from us and are in the mempool: BOOST_FOREACH(const CTxIn& txin, tx->vin) { if (txin.IsZerocoinSpend() || txin.IsSigmaSpend() || txin.IsZerocoinRemint() || txin.IsLelantusJoinSplit()) { if (!(pwallet->IsMine(txin, *tx) & ISMINE_SPENDABLE)) { return false; } } else { // Transactions not sent by us: not trusted const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.hash); if (parent == NULL) return false; const CTxOut &parentOut = parent->tx->vout[txin.prevout.n]; if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE) return false; } } return true; } bool CWalletTx::IsChange(uint32_t out) const { if (out >= tx->vout.size()) { throw std::invalid_argument("The specified output index is not valid"); } if (changes.count(out)) { return true; } // Legacy transaction handling. // Zerocoin spend have one special output mode to spend to yourself with change address, // we don't want to identify that output as change. if (!tx->IsZerocoinSpend() && ::IsMine(*pwallet, tx->vout[out].scriptPubKey)) { CTxDestination address; if (!ExtractDestination(tx->vout[out].scriptPubKey, address)) { return true; } LOCK(pwallet->cs_wallet); if (!pwallet->mapAddressBook.count(address)) { return true; } } return false; } bool CWalletTx::IsChange(const CTxOut& out) const { auto it = std::find(tx->vout.begin(), tx->vout.end(), out); if (it == tx->vout.end()) { throw std::invalid_argument("The specified output does not belong to the transaction"); } return IsChange(it - tx->vout.begin()); } bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const { CMutableTransaction tx1 = *this->tx; CMutableTransaction tx2 = *_tx.tx; for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript(); for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript(); return CTransaction(tx1) == CTransaction(tx2); } std::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman) { std::vector<uint256> result; LOCK(cs_wallet); // Sort them in chronological order std::multimap<unsigned int, CWalletTx*> mapSorted; BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet) { CWalletTx& wtx = item.second; // Don't rebroadcast if newer than nTime: if (wtx.nTimeReceived > nTime) continue; mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx)); } BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted) { CWalletTx& wtx = *item.second; if (wtx.RelayWalletTransaction(connman)) result.push_back(wtx.GetHash()); } return result; } void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) { // Do this infrequently and randomly to avoid giving away // that these are our transactions. if (GetTime() < nNextResend || !fBroadcastTransactions) return; bool fFirst = (nNextResend == 0); nNextResend = GetTime() + GetRand(30 * 60); if (fFirst) return; // Only do it if there's been a new block since last time if (nBestBlockTime < nLastResend) return; nLastResend = GetTime(); // Rebroadcast unconfirmed txes older than 5 minutes before the last // block was found: std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman); if (!relayed.empty()) LogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size()); } /** @} */ // end of mapWallet /** @defgroup Actions * * @{ */ CAmount CWallet::GetBalance(bool fExcludeLocked) const { CAmount nTotal = 0; { LOCK2(cs_main, cs_wallet); for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) { const CWalletTx* pcoin = &(*it).second; if (pcoin->IsTrusted()) nTotal += pcoin->GetAvailableCredit(true, fExcludeLocked); } } return nTotal; } std::pair<CAmount, CAmount> CWallet::GetPrivateBalance() const { size_t confirmed, unconfirmed; return GetPrivateBalance(confirmed, unconfirmed); } std::pair<CAmount, CAmount> CWallet::GetPrivateBalance(size_t &confirmed, size_t &unconfirmed) const { std::pair<CAmount, CAmount> balance = {0, 0}; confirmed = 0; unconfirmed = 0; auto zwallet = pwalletMain->zwallet.get(); if(!zwallet) return balance; auto lelantusCoins = zwallet->GetTracker().ListLelantusMints(true, false, false); for (auto const &c : lelantusCoins) { if (c.isUsed || c.isArchived || !c.isSeedCorrect) { continue; } auto conf = c.nHeight > 0 ? chainActive.Height() - c.nHeight + 1 : 0; if (conf >= ZC_MINT_CONFIRMATIONS) { confirmed++; balance.first += c.amount; } else { unconfirmed++; balance.second += c.amount; } } auto sigmaCoins = zwallet->GetTracker().ListMints(true, false, false); for (auto const &c : sigmaCoins) { if (c.isUsed || c.isArchived || !c.isSeedCorrect) { continue; } CAmount amount; if (!sigma::DenominationToInteger(c.denom, amount)) { throw std::runtime_error("Fail to get denomination value"); } auto conf = c.nHeight > 0 ? chainActive.Height() - c.nHeight + 1 : 0; if (conf >= ZC_MINT_CONFIRMATIONS) { confirmed++; balance.first += amount; } else { unconfirmed++; balance.second += amount; } } return balance; } std::vector<CRecipient> CWallet::CreateSigmaMintRecipients( std::vector<sigma::PrivateCoin>& coins, std::vector<CHDMint>& vDMints) { EnsureMintWalletAvailable(); std::vector<CRecipient> vecSend; CWalletDB walletdb(pwalletMain->strWalletFile); std::transform(coins.begin(), coins.end(), std::back_inserter(vecSend), [&vDMints, &walletdb](sigma::PrivateCoin& coin) -> CRecipient { // Generate and store secrets deterministically in the following function. CHDMint dMint; pwalletMain->zwallet->GenerateMint(walletdb, coin.getPublicCoin().getDenomination(), coin, dMint); // Get a copy of the 'public' portion of the coin. You should // embed this into a Signa 'MINT' transaction along with a series // of currency inputs totaling the assigned value of one sigma. auto& pubCoin = coin.getPublicCoin(); if (!pubCoin.validate()) { throw std::runtime_error("Unable to mint a sigma coin."); } // Create script for coin CScript scriptSerializedCoin; // opcode is inserted as 1 byte according to file script/script.h scriptSerializedCoin << OP_SIGMAMINT; // and this one will write the size in different byte lengths depending on the length of vector. If vector size is <0.4c, which is 76, will write the size of vector in just 1 byte. In our case the size is always 34, so must write that 34 in 1 byte. std::vector<unsigned char> vch = pubCoin.getValue().getvch(); scriptSerializedCoin.insert(scriptSerializedCoin.end(), vch.begin(), vch.end()); CAmount v; DenominationToInteger(pubCoin.getDenomination(), v); vDMints.push_back(dMint); return {scriptSerializedCoin, v, false}; } ); return vecSend; } CRecipient CWallet::CreateLelantusMintRecipient( lelantus::PrivateCoin& coin,
{'commit': 'diff --git a/src/miner.cpp b/src/miner.cpp\nindex 945d247d4..a534351b8 100644\n--- a/src/miner.cpp\n+++ b/src/miner.cpp\n@@ -1016,6 +1016,8 @@ void static ZcoinMiner(const CChainParams &chainparams) {\n                         free(scratchpad);\n                     }\n \n+                    boost::this_thread::interruption_point();\n+                    \n                     //LogPrintf("*****\\nhash   : %s  \\ntarget : %s\\n", UintToArith256(thash).ToString(), hashTarget.ToString());\n \n                     if (UintToArith256(thash) <= hashTarget) {\n@@ -1037,8 +1039,6 @@ void static ZcoinMiner(const CChainParams &chainparams) {\n                     if ((pblock->nNonce & 0xFF) == 0)\n                         break;\n                 }\n-                // Check for stop or if block needs to be rebuilt\n-                boost::this_thread::interruption_point();\n                 // Regtest mode doesn\'t require peers\n                 if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0 && chainparams.MiningRequiresPeers())\n                     break;\ndiff --git a/src/net.h b/src/net.h\nindex dd2b5e09d..cdfc028bc 100644\n--- a/src/net.h\n+++ b/src/net.h\n@@ -794,7 +794,6 @@ public:\n \n     // inventory based relay\n     CRollingBloomFilter filterInventoryKnown;\n-    std::vector<CInv> vInventoryToSend;\n     // Set of Dandelion transactions that should be known to this peer\n     std::set<uint256> setDandelionInventoryKnown;\n     // Set of transaction ids we still have to announce.\n@@ -806,6 +805,8 @@ public:\n     // There is no final sorting before sending, as they are always sent immediately\n     // and in the order requested.\n     std::vector<uint256> vInventoryBlockToSend;\n+    // List of non-tx/non-block inventory items\n+    std::vector<CInv> vInventoryOtherToSend;\n     CCriticalSection cs_inventory;\n     std::set<uint256> setAskFor;\n     std::vector<std::pair<int64_t, CInv>> vecAskFor;\n@@ -980,7 +981,12 @@ public:\n         } else if (inv.type == MSG_BLOCK) {\n             vInventoryBlockToSend.push_back(inv.hash);\n         } else {\n-            vInventoryToSend.push_back(inv);\n+            if (!filterInventoryKnown.contains(inv.hash)) {\n+                LogPrint("net", "PushInventory --  inv: %s peer=%d\\n", inv.ToString(), id);\n+                vInventoryOtherToSend.push_back(inv);\n+            } else {\n+                LogPrint("net", "PushInventory --  filtered inv: %s peer=%d\\n", inv.ToString(), id);\n+            }\n         }\n     }\n \ndiff --git a/src/net_processing.cpp b/src/net_processing.cpp\nindex 775d3484e..1f4628f07 100644\n--- a/src/net_processing.cpp\n+++ b/src/net_processing.cpp\n@@ -1021,15 +1021,6 @@ bool static AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main)\n     return true;\n }\n \n-static void RelayTransaction(const CTransaction& tx, CConnman& connman)\n-{\n-    CInv inv(MSG_TX, tx.GetHash());\n-    connman.ForEachNode([&inv](CNode* pnode)\n-    {\n-        pnode->PushInventory(inv);\n-    });\n-}\n-\n static void RelayAddress(const CAddress& addr, bool fReachable, CConnman& connman)\n {\n     unsigned int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)\n@@ -2203,7 +2194,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr\n             }\n \n             mempool.check(pcoinsTip);\n-            RelayTransaction(tx, connman);\n+            connman.RelayTransaction(tx);\n             for (unsigned int i = 0; i < tx.vout.size(); i++) {\n                 vWorkQueue.emplace_back(inv.hash, i);\n             }\n@@ -2257,7 +2248,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr\n                             false /* markZcoinSpendTransactionSerial */\n                         );\n \n-                        RelayTransaction(orphanTx, connman);\n+                        connman.RelayTransaction(orphanTx);\n                         for (unsigned int i = 0; i < orphanTx.vout.size(); i++) {\n                             vWorkQueue.emplace_back(orphanHash, i);\n                         }\n@@ -2312,7 +2303,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr\n             // Changes to mempool should also be made to Dandelion stempool\n             txpools.getStemTxPool().check(pcoinsTip);\n \n-            RelayTransaction(tx, connman);\n+            connman.RelayTransaction(tx);\n         }\n         else if (fMissingInputs)\n         {\n@@ -2382,7 +2373,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr\n                 int nDoS = 0;\n                 if (!state.IsInvalid(nDoS) || nDoS == 0) {\n                     LogPrintf("Force relaying tx %s from whitelisted peer=%d\\n", tx.GetHash().ToString(), pfrom->id);\n-                    RelayTransaction(tx, connman);\n+                    connman.RelayTransaction(tx);\n                 } else {\n                     LogPrintf("Not relaying invalid transaction %s from whitelisted peer=%d (%s)\\n", tx.GetHash().ToString(), pfrom->id, FormatStateMessage(state));\n                 }\n@@ -3770,30 +3761,21 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr\n                     pto->filterInventoryKnown.insert(hash);\n                 }\n             }\n-        }\n-\n-        // vInventoryToSend from dash\n-        vector<CInv> vInvWait;\n-        {\n-            LOCK(pto->cs_inventory);\n-            vInv.reserve(std::min<size_t>(1000, pto->vInventoryToSend.size()));\n-            vInvWait.reserve(pto->vInventoryToSend.size());\n-            BOOST_FOREACH(const CInv& inv, pto->vInventoryToSend)\n-            {\n-                pto->filterInventoryKnown.insert(inv.hash);\n \n-                LogPrint("net", "SendMessages -- queued inv: %s  index=%d peer=%d\\n", inv.ToString(), vInv.size(), pto->id);\n+            // Send non-tx/non-block inventory items\n+            for (const auto& inv : pto->vInventoryOtherToSend) {\n+                if (pto->filterInventoryKnown.contains(inv.hash)) {\n+                    continue;\n+                }\n                 vInv.push_back(inv);\n-                if (vInv.size() >= 1000)\n-                {\n-                    LogPrint("net", "SendMessages -- pushing inv\'s: count=%d peer=%d\\n", vInv.size(), pto->id);\n+                pto->filterInventoryKnown.insert(inv.hash);\n+                if (vInv.size() == MAX_INV_SZ) {\n                     connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));\n                     vInv.clear();\n                 }\n             }\n-            pto->vInventoryToSend = vInvWait;\n+            pto->vInventoryOtherToSend.clear();\n         }\n-        \n         if (!vInv.empty())\n             connman.PushMessage(pto, msgMaker.Make(NetMsgType::INV, vInv));\n \ndiff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp\nindex 3f0dd6804..cddadb2cd 100644\n--- a/src/rpc/mining.cpp\n+++ b/src/rpc/mining.cpp\n@@ -124,9 +124,20 @@ UniValue generateBlocks(boost::shared_ptr<CReserveScript> coinbaseScript, int nG\n             LOCK(cs_main);\n             IncrementExtraNonce(pblock, chainActive.Tip(), nExtraNonce);\n         }\n-        while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {\n-            ++pblock->nNonce;\n-            --nMaxTries;\n+        if (pblock->IsMTP()) {\n+            while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount) {\n+                pblock->mtpHashValue = mtp::hash(*pblock, Params().GetConsensus().powLimit);\n+                if (CheckProofOfWork(pblock->mtpHashValue, pblock->nBits, Params().GetConsensus()))\n+                    break;\n+                ++pblock->nNonce;\n+                --nMaxTries;\n+            }\n+        }\n+        else {\n+            while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount && !CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) {\n+                ++pblock->nNonce;\n+                --nMaxTries;\n+            }\n         }\n         if (nMaxTries == 0) {\n             break;\ndiff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp\nindex 014ec8657..036d439bd 100644\n--- a/src/wallet/wallet.cpp\n+++ b/src/wallet/wallet.cpp\n@@ -2107,10 +2107,13 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman)\n                 //    GetHash().ToString(), (nEmbargo - nCurrTime) / 1000000);\n                 CInv inv(MSG_DANDELION_TX, GetHash());\n                 return CNode::localDandelionDestinationPushInventory(inv);\n-            } else {\n+            }\n+            else {\n                 // LogPrintf("Relaying wtx %s\\n", GetHash().ToString());\n-                g_connman->RelayTransaction(*this);\n-                return true;\n+                if (connman) {\n+                    connman->RelayTransaction(*this);\n+                    return true;\n+                }\n             }\n         }\n     }\n', 'commit_subject': "Merge branch 'core14' of github.com:zcoinofficial/zcoin into core14", 'repo_name': 'zcoinofficial/zcoin', 'committer': {'name': 'Andrey', 'email': 'fd2b0a636ed0c80c1646cd2c2e72f7a758b42b5b@zcoin.io', 'time_sec': '1587458937', 'tz_offset': '240', 'date': {'seconds': '1587458937'}}, 'commit_tree': '951824c8a91eda8f8d4ae8e58e7761e7f66c1755', 'commit_message': "Merge branch 'core14' of github.com:zcoinofficial/zcoin into core14\n", 'language_name': 'Shell', 'license': 'mit', 'hunks': '@@ -2107,10 +2107,13 @@ bool CWalletTx::RelayWalletTransaction(CConnman* connman)\n                 //    GetHash().ToString(), (nEmbargo - nCurrTime) / 1000000);\n                 CInv inv(MSG_DANDELION_TX, GetHash());\n                 return CNode::localDandelionDestinationPushInventory(inv);\n-            } else {\n+            }\n+            else {\n                 // LogPrintf("Relaying wtx %s\\n", GetHash().ToString());\n-                g_connman->RelayTransaction(*this);\n-                return true;\n+                if (connman) {\n+                    connman->RelayTransaction(*this);\n+                    return true;\n+                }\n             }\n         }\n     }\n', 'addition_count': 6, 'deletion_count': 3, 'src_file': 'a/src/wallet/wallet.cpp', 'tgt_file': 'b/src/wallet/wallet.cpp', 'file_extension': '.cpp', 'before_file': '// Copyright (c) 2009-2010 Satoshi Nakamoto\n// Copyright (c) 2009-2016 The Bitcoin Core developers\n// Distributed under the MIT software license, see the accompanying\n// file COPYING or http://www.opensource.org/licenses/mit-license.php.\n\n#include "wallet.h"\n#include "walletexcept.h"\n#include "sigmaspendbuilder.h"\n#include "lelantusjoinsplitbuilder.h"\n#include "amount.h"\n#include "base58.h"\n#include "checkpoints.h"\n#include "chain.h"\n#include "wallet/coincontrol.h"\n#include "consensus/consensus.h"\n#include "consensus/validation.h"\n#include "key.h"\n#include "keystore.h"\n#include "validation.h"\n#include "sigma.h"\n#include "../sigma/coinspend.h"\n#include "../sigma/spend_metadata.h"\n#include "../sigma/coin.h"\n#include "lelantus.h"\n#include "llmq/quorums_instantsend.h"\n#include "llmq/quorums_chainlocks.h"\n#include "net.h"\n#include "policy/policy.h"\n#include "primitives/block.h"\n#include "primitives/transaction.h"\n#include "script/script.h"\n#include "script/sign.h"\n#include "timedata.h"\n#include "txmempool.h"\n#include "util.h"\n#include "ui_interface.h"\n#include "utilmoneystr.h"\n#include "validation.h"\n#include "masternode-sync.h"\n#include "random.h"\n#include "init.h"\n#include "hdmint/wallet.h"\n#include "rpc/protocol.h"\n\n#include "crypto/hmac_sha512.h"\n#include "crypto/aes.h"\n\n#include "hdmint/tracker.h"\n\n#include "evo/deterministicmns.h"\n\n#include <assert.h>\n#include <boost/algorithm/string.hpp>\n#include <boost/algorithm/string/replace.hpp>\n#include <boost/filesystem.hpp>\n#include <boost/thread.hpp>\n#include <vector>\n\n#include "bip47/account.h"\n#include "bip47/paymentcode.h"\n#include "bip47/bip47utils.h"\n\nCWallet* pwalletMain = NULL;\n\n/** Transaction fee set by the user */\nCFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);\nunsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;\nbool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;\nbool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;\nbool fWalletRbf = DEFAULT_WALLET_RBF;\n\nconst char * DEFAULT_WALLET_DAT = "wallet.dat";\n\n/**\n * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)\n * Override with -mintxfee\n */\nCFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);\n/**\n * If fee estimation does not have enough data to provide estimates, use this fee instead.\n * Has no effect if not using fee estimation\n * Override with -fallbackfee\n */\nCFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);\n\nconst uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));\n\n/** @defgroup mapWallet\n *\n * @{\n */\n\nstruct CompareValueOnly\n{\n    bool operator()(const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t1,\n                    const std::pair<CAmount, std::pair<const CWalletTx*, unsigned int> >& t2) const\n    {\n        return t1.first < t2.first;\n    }\n};\n\nstruct CompareByAmount\n{\n    bool operator()(const CompactTallyItem& t1, const CompactTallyItem& t2) const\n    {\n        return t1.nAmount > t2.nAmount;\n    }\n};\n\nstatic void EnsureMintWalletAvailable()\n{\n    if (!pwalletMain || !pwalletMain->zwallet) {\n        throw std::logic_error("Sigma feature requires HD wallet");\n    }\n}\n\nstd::string COutput::ToString() const {\n    return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString(), i, nDepth, FormatMoney(tx->tx->vout[i].nValue));\n}\n\nclass CAffectedKeysVisitor : public boost::static_visitor<void> {\nprivate:\n    const CKeyStore &keystore;\n    std::vector<CKeyID> &vKeys;\n\npublic:\n    CAffectedKeysVisitor(const CKeyStore &keystoreIn, std::vector<CKeyID> &vKeysIn) : keystore(keystoreIn), vKeys(vKeysIn) {}\n\n    void Process(const CScript &script) {\n        txnouttype type;\n        std::vector<CTxDestination> vDest;\n        int nRequired;\n        if (ExtractDestinations(script, type, vDest, nRequired)) {\n            BOOST_FOREACH(const CTxDestination &dest, vDest)\n                boost::apply_visitor(*this, dest);\n        }\n    }\n\n    void operator()(const CKeyID &keyId) {\n        if (keystore.HaveKey(keyId))\n            vKeys.push_back(keyId);\n    }\n\n    void operator()(const CScriptID &scriptId) {\n        CScript script;\n        if (keystore.GetCScript(scriptId, script))\n            Process(script);\n    }\n\n    void operator()(const CNoDestination &none) {}\n};\n\nconst CWalletTx *CWallet::GetWalletTx(const uint256 &hash) const {\n    LOCK(cs_wallet);\n    std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(hash);\n    if (it == mapWallet.end())\n        return NULL;\n    return &(it->second);\n}\n\nCPubKey CWallet::GetKeyFromKeypath(uint32_t nChange, uint32_t nChild, CKey& secret) {\n    AssertLockHeld(cs_wallet); // mapKeyMetadata\n\n    uint32_t nIndex = Params().GetConsensus().IsMain() ? BIP44_FIRO_INDEX : BIP44_TEST_INDEX;\n\n    // Fail if not using HD wallet (no keypaths)\n    if (hdChain.masterKeyID.IsNull())\n        throw std::runtime_error(std::string(__func__) + ": Non-HD wallet detected");\n\n    // use BIP44 keypath: m / purpose\' / coin_type\' / account\' / change / address_index\n    CKey key;                      //master key seed (256bit)\n    CExtKey masterKey;             //hd master key\n    CExtKey purposeKey;            //key at m/44\'\n    CExtKey coinTypeKey;           //key at m/44\'/<1/136>\' (Testnet or Firo Coin Type respectively, according to SLIP-0044)\n    CExtKey accountKey;            //key at m/44\'/<1/136>\'/0\'\n    CExtKey externalChainChildKey; //key at m/44\'/<1/136>\'/0\'/<c> (Standard: 0/1, Mints: 2)\n    CExtKey childKey;              //key at m/44\'/<1/136>\'/0\'/<c>/<n>\n\n    if(hdChain.nVersion >= CHDChain::VERSION_WITH_BIP39){\n        MnemonicContainer mContainer = mnemonicContainer;\n        DecryptMnemonicContainer(mContainer);\n        SecureVector seed = mContainer.GetSeed();\n        masterKey.SetMaster(&seed[0], seed.size());\n    } else {\n        // try to get the master key\n        if (!GetKey(hdChain.masterKeyID, key))\n            throw std::runtime_error(std::string(__func__) + ": Master key not found");\n        masterKey.SetMaster(key.begin(), key.size());\n    }\n\n    // derive m/44\'\n    // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)\n    masterKey.Derive(purposeKey, BIP44_INDEX | BIP32_HARDENED_KEY_LIMIT);\n\n    // derive m/44\'/136\'\n    purposeKey.Derive(coinTypeKey, nIndex | BIP32_HARDENED_KEY_LIMIT);\n\n    // derive m/44\'/136\'/0\'\n    coinTypeKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);\n\n    // derive m/44\'/136\'/0\'/<c>\n    accountKey.Derive(externalChainChildKey, nChange);\n\n    // derive m/44\'/136\'/0\'/<c>/<n>\n    externalChainChildKey.Derive(childKey, nChild);\n\n    secret = childKey.key;\n\n    CPubKey pubkey = secret.GetPubKey();\n    assert(secret.VerifyPubKey(pubkey));\n\n    return pubkey;\n}\n\nCPubKey CWallet::GenerateNewKey(uint32_t nChange, bool fWriteChain)\n{\n    AssertLockHeld(cs_wallet); // mapKeyMetadata\n    bool fCompressed = CanSupportFeature(FEATURE_COMPRPUBKEY); // default to compressed public keys if we want 0.6.0 wallets\n\n    CKey secret;\n\n    // Create new metadata\n    int64_t nCreationTime = GetTime();\n    CKeyMetadata metadata(nCreationTime);\n    metadata.nChange = Component(nChange, false);\n\n    uint32_t nIndex = Params().GetConsensus().IsMain() ? BIP44_FIRO_INDEX : BIP44_TEST_INDEX;\n\n    // use HD key derivation if HD was enabled during wallet creation\n    // TODO: change code to foloow bitcoin structure more closely\n    if (IsHDEnabled()) {\n        // use BIP44 keypath: m / purpose\' / coin_type\' / account\' / change / address_index\n        CKey key;                      //master key seed (256bit)\n        CExtKey masterKey;             //hd master key\n        CExtKey purposeKey;            //key at m/44\'\n        CExtKey coinTypeKey;           //key at m/44\'/<1/136>\' (Testnet or Firo Coin Type respectively, according to SLIP-0044)\n        CExtKey accountKey;            //key at m/44\'/<1/136>\'/0\'\n        CExtKey externalChainChildKey; //key at m/44\'/<1/136>\'/0\'/<c> (Standard: 0/1, Mints: 2)\n        CExtKey childKey;              //key at m/44\'/<1/136>\'/0\'/<c>/<n>\n        //For bip39 we use it\'s original way for generating keys to make it compatible with hardware and software wallets\n        if(hdChain.nVersion >= CHDChain::VERSION_WITH_BIP39){\n            MnemonicContainer mContainer = mnemonicContainer;\n            DecryptMnemonicContainer(mContainer);\n            SecureVector seed = mContainer.GetSeed();\n            masterKey.SetMaster(&seed[0], seed.size());\n        } else {\n            // try to get the master key\n            if (!GetKey(hdChain.masterKeyID, key))\n                throw std::runtime_error(std::string(__func__) + ": Master key not found");\n            masterKey.SetMaster(key.begin(), key.size());\n        }\n\n        // derive m/44\'\n        // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)\n        masterKey.Derive(purposeKey, BIP44_INDEX | BIP32_HARDENED_KEY_LIMIT);\n\n        // derive m/44\'/136\'\n        purposeKey.Derive(coinTypeKey, nIndex | BIP32_HARDENED_KEY_LIMIT);\n\n        // derive m/44\'/136\'/0\'\n        coinTypeKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);\n\n        // derive m/44\'/136\'/0\'/<c>\n        accountKey.Derive(externalChainChildKey, nChange);\n\n        // derive child key at next index, skip keys already known to the wallet\n        do\n        {\n            externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounters[nChange]);\n            metadata.hdKeypath = "m/44\'/" + std::to_string(nIndex) + "\'/0\'/" + std::to_string(nChange) + "/" + std::to_string(hdChain.nExternalChainCounters[nChange]);\n            metadata.hdMasterKeyID = hdChain.masterKeyID;\n            metadata.nChild = Component(hdChain.nExternalChainCounters[nChange], false);\n            // increment childkey index\n            hdChain.nExternalChainCounters[nChange]++;\n        } while (HaveKey(childKey.key.GetPubKey().GetID()));\n        secret = childKey.key;\n\n        // update the chain model in the database\n        if(fWriteChain){\n            if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))\n                throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");\n        }\n    /* bitcoin 0.14:\n    if (IsHDEnabled()) {\n        DeriveNewChildKey(metadata, secret);\n    */\n    } else {\n        secret.MakeNewKey(fCompressed);\n    }\n\n    // Compressed public keys were introduced in version 0.6.0\n    if (fCompressed)\n        SetMinVersion(FEATURE_COMPRPUBKEY);\n\n    CPubKey pubkey = secret.GetPubKey();\n    assert(secret.VerifyPubKey(pubkey));\n\n    mapKeyMetadata[pubkey.GetID()] = metadata;\n    UpdateTimeFirstKey(nCreationTime);\n\n    if (!AddKeyPubKey(secret, pubkey))\n        throw std::runtime_error(std::string(__func__) + ": AddKey failed");\n    return pubkey;\n}\n\nvoid CWallet::DeriveNewChildKey(CKeyMetadata& metadata, CKey& secret)\n{\n    // for now we use a fixed keypath scheme of m/0\'/0\'/k\n    CKey key;                      //master key seed (256bit)\n    CExtKey masterKey;             //hd master key\n    CExtKey accountKey;            //key at m/0\'\n    CExtKey externalChainChildKey; //key at m/0\'/0\'\n    CExtKey childKey;              //key at m/0\'/0\'/<n>\'\n\n    // try to get the master key\n    if (!GetKey(hdChain.masterKeyID, key))\n        throw std::runtime_error(std::string(__func__) + ": Master key not found");\n\n    masterKey.SetMaster(key.begin(), key.size());\n\n    // derive m/0\'\n    // use hardened derivation (child keys >= 0x80000000 are hardened after bip32)\n    masterKey.Derive(accountKey, BIP32_HARDENED_KEY_LIMIT);\n\n    // derive m/0\'/0\'\n    accountKey.Derive(externalChainChildKey, BIP32_HARDENED_KEY_LIMIT);\n\n    // derive child key at next index, skip keys already known to the wallet\n    do {\n        // always derive hardened keys\n        // childIndex | BIP32_HARDENED_KEY_LIMIT = derive childIndex in hardened child-index-range\n        // example: 1 | BIP32_HARDENED_KEY_LIMIT == 0x80000001 == 2147483649\n        externalChainChildKey.Derive(childKey, hdChain.nExternalChainCounter | BIP32_HARDENED_KEY_LIMIT);\n        metadata.hdKeypath = "m/0\'/0\'/" + std::to_string(hdChain.nExternalChainCounter) + "\'";\n        metadata.hdMasterKeyID = hdChain.masterKeyID;\n        // increment childkey index\n        hdChain.nExternalChainCounter++;\n    } while (HaveKey(childKey.key.GetPubKey().GetID()));\n    secret = childKey.key;\n\n    // update the chain model in the database\n    if (!CWalletDB(strWalletFile).WriteHDChain(hdChain))\n        throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed");\n}\n\nbool CWallet::AddKeyPubKey(const CKey& secret, const CPubKey &pubkey)\n{\n    AssertLockHeld(cs_wallet); // mapKeyMetadata\n    if (!CCryptoKeyStore::AddKeyPubKey(secret, pubkey))\n        return false;\n\n    // check if we need to remove from watch-only\n    CScript script;\n    script = GetScriptForDestination(pubkey.GetID());\n    if (HaveWatchOnly(script))\n        RemoveWatchOnly(script);\n    script = GetScriptForRawPubKey(pubkey);\n    if (HaveWatchOnly(script))\n        RemoveWatchOnly(script);\n\n    if (!fFileBacked)\n        return true;\n    if (!IsCrypted()) {\n        return CWalletDB(strWalletFile).WriteKey(pubkey,\n                                                 secret.GetPrivKey(),\n                                                 mapKeyMetadata[pubkey.GetID()]);\n    }\n    return true;\n}\n\nbool CWallet::AddCryptedKey(const CPubKey &vchPubKey,\n                            const std::vector<unsigned char> &vchCryptedSecret)\n{\n    if (!CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret))\n        return false;\n    if (!fFileBacked)\n        return true;\n    {\n        LOCK(cs_wallet);\n        if (pwalletdbEncryption)\n            return pwalletdbEncryption->WriteCryptedKey(vchPubKey,\n                                                        vchCryptedSecret,\n                                                        mapKeyMetadata[vchPubKey.GetID()]);\n        else\n            return CWalletDB(strWalletFile).WriteCryptedKey(vchPubKey,\n                                                            vchCryptedSecret,\n                                                            mapKeyMetadata[vchPubKey.GetID()]);\n    }\n    return false;\n}\n\nbool CWallet::LoadKeyMetadata(const CTxDestination& keyID, const CKeyMetadata &meta)\n{\n    AssertLockHeld(cs_wallet); // mapKeyMetadata\n    UpdateTimeFirstKey(meta.nCreateTime);\n    mapKeyMetadata[keyID] = meta;\n    return true;\n}\n\nbool CWallet::LoadCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret)\n{\n    return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret);\n}\n\nvoid CWallet::UpdateTimeFirstKey(int64_t nCreateTime)\n{\n    AssertLockHeld(cs_wallet);\n    if (nCreateTime <= 1) {\n        // Cannot determine birthday information, so set the wallet birthday to\n        // the beginning of time.\n        nTimeFirstKey = 1;\n    } else if (!nTimeFirstKey || nCreateTime < nTimeFirstKey) {\n        nTimeFirstKey = nCreateTime;\n    }\n}\n\nbool CWallet::AddCScript(const CScript& redeemScript)\n{\n    if (!CCryptoKeyStore::AddCScript(redeemScript))\n        return false;\n    if (!fFileBacked)\n        return true;\n    return CWalletDB(strWalletFile).WriteCScript(Hash160(redeemScript), redeemScript);\n}\n\nbool CWallet::LoadCScript(const CScript& redeemScript)\n{\n    /* A sanity check was added in pull #3843 to avoid adding redeemScripts\n     * that never can be redeemed. However, old wallets may still contain\n     * these. Do not add them to the wallet and warn. */\n    if (redeemScript.size() > MAX_SCRIPT_ELEMENT_SIZE)\n    {\n        std::string strAddr = CBitcoinAddress(CScriptID(redeemScript)).ToString();\n        LogPrintf("%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",\n            __func__, redeemScript.size(), MAX_SCRIPT_ELEMENT_SIZE, strAddr);\n        return true;\n    }\n\n    return CCryptoKeyStore::AddCScript(redeemScript);\n}\n\nbool CWallet::AddWatchOnly(const CScript& dest)\n{\n    if (!CCryptoKeyStore::AddWatchOnly(dest))\n        return false;\n    const CKeyMetadata& meta = mapKeyMetadata[CScriptID(dest)];\n    UpdateTimeFirstKey(meta.nCreateTime);\n    NotifyWatchonlyChanged(true);\n    if (!fFileBacked)\n        return true;\n    return CWalletDB(strWalletFile).WriteWatchOnly(dest, meta);\n}\n\nbool CWallet::AddWatchOnly(const CScript& dest, int64_t nCreateTime)\n{\n    mapKeyMetadata[CScriptID(dest)].nCreateTime = nCreateTime;\n    return AddWatchOnly(dest);\n}\n\nbool CWallet::RemoveWatchOnly(const CScript &dest)\n{\n    AssertLockHeld(cs_wallet);\n    if (!CCryptoKeyStore::RemoveWatchOnly(dest))\n        return false;\n    if (!HaveWatchOnly())\n        NotifyWatchonlyChanged(false);\n    if (fFileBacked)\n        if (!CWalletDB(strWalletFile).EraseWatchOnly(dest))\n            return false;\n\n    return true;\n}\n\nbool CWallet::LoadWatchOnly(const CScript &dest)\n{\n    return CCryptoKeyStore::AddWatchOnly(dest);\n}\n\nbool CWallet::Unlock(const SecureString &strWalletPassphrase, const bool& fFirstUnlock)\n{\n    CCrypter crypter;\n    CKeyingMaterial vMasterKey;\n\n    {\n        LOCK(cs_wallet);\n        BOOST_FOREACH(const MasterKeyMap::value_type& pMasterKey, mapMasterKeys)\n        {\n            if(!crypter.SetKeyFromPassphrase(strWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))\n                return false;\n            if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))\n                continue; // try another master key\n            if (CCryptoKeyStore::Unlock(vMasterKey, fFirstUnlock))\n                return true;\n        }\n    }\n    return false;\n}\n\nbool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)\n{\n    bool fWasLocked = IsLocked();\n\n    {\n        LOCK(cs_wallet);\n        Lock();\n\n        CCrypter crypter;\n        CKeyingMaterial vMasterKey;\n        BOOST_FOREACH(MasterKeyMap::value_type& pMasterKey, mapMasterKeys)\n        {\n            if(!crypter.SetKeyFromPassphrase(strOldWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))\n                return false;\n            if (!crypter.Decrypt(pMasterKey.second.vchCryptedKey, vMasterKey))\n                return false;\n            if (CCryptoKeyStore::Unlock(vMasterKey))\n            {\n                int64_t nStartTime = GetTimeMillis();\n                crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);\n                pMasterKey.second.nDeriveIterations = pMasterKey.second.nDeriveIterations * (100 / ((double)(GetTimeMillis() - nStartTime)));\n\n                nStartTime = GetTimeMillis();\n                crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod);\n                pMasterKey.second.nDeriveIterations = (pMasterKey.second.nDeriveIterations + pMasterKey.second.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;\n\n                if (pMasterKey.second.nDeriveIterations < 25000)\n                    pMasterKey.second.nDeriveIterations = 25000;\n\n                LogPrintf("Wallet passphrase changed to an nDeriveIterations of %i\\n", pMasterKey.second.nDeriveIterations);\n\n                if (!crypter.SetKeyFromPassphrase(strNewWalletPassphrase, pMasterKey.second.vchSalt, pMasterKey.second.nDeriveIterations, pMasterKey.second.nDerivationMethod))\n                    return false;\n                if (!crypter.Encrypt(vMasterKey, pMasterKey.second.vchCryptedKey))\n                    return false;\n                CWalletDB(strWalletFile).WriteMasterKey(pMasterKey.first, pMasterKey.second);\n                if (fWasLocked)\n                    Lock();\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nvoid CWallet::SetBestChain(const CBlockLocator& loc)\n{\n    CWalletDB walletdb(strWalletFile);\n    walletdb.WriteBestBlock(loc);\n}\n\nbool CWallet::SetMinVersion(enum WalletFeature nVersion, CWalletDB* pwalletdbIn, bool fExplicit)\n{\n    LOCK(cs_wallet); // nWalletVersion\n    if (nWalletVersion >= nVersion)\n        return true;\n\n    // when doing an explicit upgrade, if we pass the max version permitted, upgrade all the way\n    if (fExplicit && nVersion > nWalletMaxVersion)\n            nVersion = FEATURE_LATEST;\n\n    nWalletVersion = nVersion;\n\n    if (nVersion > nWalletMaxVersion)\n        nWalletMaxVersion = nVersion;\n\n    if (fFileBacked)\n    {\n        CWalletDB* pwalletdb = pwalletdbIn ? pwalletdbIn : new CWalletDB(strWalletFile);\n        if (nWalletVersion > 40000)\n            pwalletdb->WriteMinVersion(nWalletVersion);\n        if (!pwalletdbIn)\n            delete pwalletdb;\n    }\n\n    return true;\n}\n\nbool CWallet::SetMaxVersion(int nVersion)\n{\n    LOCK(cs_wallet); // nWalletVersion, nWalletMaxVersion\n    // cannot downgrade below current version\n    if (nWalletVersion > nVersion)\n        return false;\n\n    nWalletMaxVersion = nVersion;\n\n    return true;\n}\n\nstd::set<uint256> CWallet::GetConflicts(const uint256& txid) const\n{\n    std::set<uint256> result;\n    AssertLockHeld(cs_wallet);\n\n    std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(txid);\n    if (it == mapWallet.end())\n        return result;\n    const CWalletTx& wtx = it->second;\n\n    std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;\n\n    BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)\n    {\n        if (mapTxSpends.count(txin.prevout) <= 1)\n            continue;  // No conflict if zero or one spends\n        range = mapTxSpends.equal_range(txin.prevout);\n        for (TxSpends::const_iterator _it = range.first; _it != range.second; ++_it)\n            result.insert(_it->second);\n    }\n    return result;\n}\n\nbool CWallet::HasWalletSpend(const uint256& txid) const\n{\n    AssertLockHeld(cs_wallet);\n    auto iter = mapTxSpends.lower_bound(COutPoint(txid, 0));\n    return (iter != mapTxSpends.end() && iter->first.hash == txid);\n}\n\nvoid CWallet::Flush(bool shutdown)\n{\n    bitdb.Flush(shutdown);\n}\n\nbool CWallet::Verify()\n{\n    if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))\n        return true;\n\n    LogPrintf("Using BerkeleyDB version %s\\n", DbEnv::version(0, 0, 0));\n    std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);\n\n    LogPrintf("Using wallet %s\\n", walletFile);\n    uiInterface.InitMessage(_("Verifying wallet..."));\n\n    // Wallet file must be a plain filename without a directory\n    if (walletFile != boost::filesystem::basename(walletFile) + boost::filesystem::extension(walletFile))\n        return InitError(strprintf(_("Wallet %s resides outside data directory %s"), walletFile, GetDataDir().string()));\n\n    if (!bitdb.Open(GetDataDir()))\n    {\n        // try moving the database env out of the way\n        boost::filesystem::path pathDatabase = GetDataDir() / "database";\n        boost::filesystem::path pathDatabaseBak = GetDataDir() / strprintf("database.%d.bak", GetTime());\n        try {\n            boost::filesystem::rename(pathDatabase, pathDatabaseBak);\n            LogPrintf("Moved old %s to %s. Retrying.\\n", pathDatabase.string(), pathDatabaseBak.string());\n        } catch (const boost::filesystem::filesystem_error&) {\n            // failure is ok (well, not really, but it\'s not worse than what we started with)\n        }\n\n        // try again\n        if (!bitdb.Open(GetDataDir())) {\n            // if it still fails, it probably means we can\'t even create the database env\n            return InitError(strprintf(_("Error initializing wallet database environment %s!"), GetDataDir()));\n        }\n    }\n\n    if (GetBoolArg("-salvagewallet", false))\n    {\n        // Recover readable keypairs:\n        if (!CWalletDB::Recover(bitdb, walletFile, true))\n            return false;\n    }\n\n    if (boost::filesystem::exists(GetDataDir() / walletFile))\n    {\n        CDBEnv::VerifyResult r = bitdb.Verify(walletFile, CWalletDB::Recover);\n        if (r == CDBEnv::RECOVER_OK)\n        {\n            InitWarning(strprintf(_("Warning: Wallet file corrupt, data salvaged!"\n                                         " Original %s saved as %s in %s; if"\n                                         " your balance or transactions are incorrect you should"\n                                         " restore from a backup."),\n                walletFile, "wallet.{timestamp}.bak", GetDataDir()));\n        }\n        if (r == CDBEnv::RECOVER_FAIL)\n            return InitError(strprintf(_("%s corrupt, salvage failed"), walletFile));\n    }\n\n    return true;\n}\n\nvoid CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> range)\n{\n    // We want all the wallet transactions in range to have the same metadata as\n    // the oldest (smallest nOrderPos).\n    // So: find smallest nOrderPos:\n\n    int nMinOrderPos = std::numeric_limits<int>::max();\n    const CWalletTx* copyFrom = NULL;\n    for (TxSpends::iterator it = range.first; it != range.second; ++it)\n    {\n        const uint256& hash = it->second;\n        int n = mapWallet[hash].nOrderPos;\n        if (n < nMinOrderPos)\n        {\n            nMinOrderPos = n;\n            copyFrom = &mapWallet[hash];\n        }\n    }\n    // Now copy data from copyFrom to rest:\n    for (TxSpends::iterator it = range.first; it != range.second; ++it)\n    {\n        const uint256& hash = it->second;\n        CWalletTx* copyTo = &mapWallet[hash];\n        if (copyFrom == copyTo) continue;\n        if (!copyFrom->IsEquivalentTo(*copyTo)) continue;\n        copyTo->mapValue = copyFrom->mapValue;\n        copyTo->vOrderForm = copyFrom->vOrderForm;\n        // fTimeReceivedIsTxTime not copied on purpose\n        // nTimeReceived not copied on purpose\n        copyTo->nTimeSmart = copyFrom->nTimeSmart;\n        copyTo->fFromMe = copyFrom->fFromMe;\n        copyTo->strFromAccount = copyFrom->strFromAccount;\n        // nOrderPos not copied on purpose\n        // cached members not copied on purpose\n    }\n}\n\n/**\n * Outpoint is spent if any non-conflicted transaction\n * spends it:\n */\nbool CWallet::IsSpent(const uint256 &hash, unsigned int n) const\n{\n    auto tx = GetWalletTx(hash);\n\n    // Try to handle mint output first.\n    if (tx && tx->tx->vout.size() > n) {\n        LOCK(cs_wallet);\n\n        auto& script = tx->tx->vout[n].scriptPubKey;\n        CWalletDB db(strWalletFile);\n\n        if (script.IsZerocoinMint()) {\n            return true;\n        } else if (zwallet && script.IsSigmaMint()) {\n            auto pub = sigma::ParseSigmaMintScript(script);\n            uint256 hashPubcoin = primitives::GetPubCoinValueHash(pub);\n            CMintMeta meta;\n            if(!zwallet->GetTracker().GetMetaFromPubcoin(hashPubcoin, meta)){\n                return false;\n            }\n            return meta.isUsed;\n        } else if (zwallet && (script.IsLelantusMint() || script.IsLelantusJMint())) {\n            secp_primitives::GroupElement pubcoin;\n            try {\n                lelantus::ParseLelantusMintScript(script, pubcoin);\n            } catch (std::invalid_argument &) {\n                return false;\n            }\n            uint256 hashPubcoin = primitives::GetPubCoinValueHash(pubcoin);\n            CLelantusMintMeta meta;\n            if(!zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, meta)){\n                return false;\n            }\n            return meta.isUsed;\n        }\n    }\n\n    // Normal output.\n    const COutPoint outpoint(hash, n);\n    std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range;\n    range = mapTxSpends.equal_range(outpoint);\n\n    for (TxSpends::const_iterator it = range.first; it != range.second; ++it)\n    {\n        const uint256& wtxid = it->second;\n        std::map<uint256, CWalletTx>::const_iterator mit = mapWallet.find(wtxid);\n        if (mit != mapWallet.end()) {\n            int depth = mit->second.GetDepthInMainChain();\n            if (depth > 0  || (depth == 0 && !mit->second.isAbandoned()))\n                return true; // Spent\n        }\n    }\n    return false;\n}\n\nvoid CWallet::AddToSpends(const COutPoint& outpoint, const uint256& wtxid)\n{\n    mapTxSpends.insert(std::make_pair(outpoint, wtxid));\n    setWalletUTXO.erase(outpoint);\n\n    std::pair<TxSpends::iterator, TxSpends::iterator> range;\n    range = mapTxSpends.equal_range(outpoint);\n    SyncMetaData(range);\n}\n\n\nvoid CWallet::AddToSpends(const uint256& wtxid)\n{\n    assert(mapWallet.count(wtxid));\n    CWalletTx& thisTx = mapWallet[wtxid];\n    if (thisTx.IsCoinBase()) // Coinbases don\'t spend anything!\n        return;\n\n    BOOST_FOREACH(const CTxIn& txin, thisTx.tx->vin) {\n        if (!txin.IsZerocoinSpend() && !txin.IsSigmaSpend() && !txin.IsLelantusJoinSplit()) {\n            AddToSpends(txin.prevout, wtxid);\n        }\n    }\n}\n\nbool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)\n{\n    if (IsCrypted())\n        return false;\n\n    CKeyingMaterial vMasterKey;\n\n    vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE);\n    GetStrongRandBytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE);\n\n    CMasterKey kMasterKey;\n\n    kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE);\n    GetStrongRandBytes(&kMasterKey.vchSalt[0], WALLET_CRYPTO_SALT_SIZE);\n\n    CCrypter crypter;\n    int64_t nStartTime = GetTimeMillis();\n    crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, 25000, kMasterKey.nDerivationMethod);\n    kMasterKey.nDeriveIterations = 2500000 / ((double)(GetTimeMillis() - nStartTime));\n\n    nStartTime = GetTimeMillis();\n    crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod);\n    kMasterKey.nDeriveIterations = (kMasterKey.nDeriveIterations + kMasterKey.nDeriveIterations * 100 / ((double)(GetTimeMillis() - nStartTime))) / 2;\n\n    if (kMasterKey.nDeriveIterations < 25000)\n        kMasterKey.nDeriveIterations = 25000;\n\n    LogPrintf("Encrypting Wallet with an nDeriveIterations of %i\\n", kMasterKey.nDeriveIterations);\n\n    if (!crypter.SetKeyFromPassphrase(strWalletPassphrase, kMasterKey.vchSalt, kMasterKey.nDeriveIterations, kMasterKey.nDerivationMethod))\n        return false;\n    if (!crypter.Encrypt(vMasterKey, kMasterKey.vchCryptedKey))\n        return false;\n\n    {\n        LOCK(cs_wallet);\n        mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;\n        if (fFileBacked)\n        {\n            assert(!pwalletdbEncryption);\n            pwalletdbEncryption = new CWalletDB(strWalletFile);\n            if (!pwalletdbEncryption->TxnBegin()) {\n                delete pwalletdbEncryption;\n                pwalletdbEncryption = NULL;\n                return false;\n            }\n            pwalletdbEncryption->WriteMasterKey(nMasterKeyMaxID, kMasterKey);\n        }\n\n        if (!EncryptKeys(vMasterKey))\n        {\n            if (fFileBacked) {\n                pwalletdbEncryption->TxnAbort();\n                delete pwalletdbEncryption;\n            }\n            // We now probably have half of our keys encrypted in memory, and half not...\n            // die and let the user reload the unencrypted wallet.\n            assert(false);\n        }\n\n        // Encryption was introduced in version 0.4.0\n        SetMinVersion(FEATURE_WALLETCRYPT, pwalletdbEncryption, true);\n\n        if (fFileBacked)\n        {\n            if (!pwalletdbEncryption->TxnCommit()) {\n                delete pwalletdbEncryption;\n                // We now have keys encrypted in memory, but not on disk...\n                // die to avoid confusion and let the user reload the unencrypted wallet.\n                assert(false);\n            }\n\n            delete pwalletdbEncryption;\n            pwalletdbEncryption = NULL;\n        }\n\n        Lock();\n        Unlock(strWalletPassphrase, true);\n\n        if(!mnemonicContainer.IsNull() && hdChain.nVersion >= CHDChain::VERSION_WITH_BIP39) {\n            assert(EncryptMnemonicContainer(vMasterKey));\n            SetMinVersion(FEATURE_HD);\n            assert(SetMnemonicContainer(mnemonicContainer, false));\n            TopUpKeyPool();\n        }\n\n        Lock();\n\n        // Need to completely rewrite the wallet file; if we don\'t, bdb might keep\n        // bits of the unencrypted private key in slack space in the database file.\n        CDB::Rewrite(strWalletFile);\n\n    }\n    NotifyStatusChanged(this);\n\n    return true;\n}\n\nDBErrors CWallet::ReorderTransactions()\n{\n    LOCK(cs_wallet);\n    CWalletDB walletdb(strWalletFile);\n\n    // Old wallets didn\'t have any defined order for transactions\n    // Probably a bad idea to change the output of this\n\n    // First: get all CWalletTx and CAccountingEntry into a sorted-by-time multimap.\n    typedef std::pair<CWalletTx*, CAccountingEntry*> TxPair;\n    typedef std::multimap<int64_t, TxPair > TxItems;\n    TxItems txByTime;\n\n    for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\n    {\n        CWalletTx* wtx = &((*it).second);\n        txByTime.insert(make_pair(wtx->nTimeReceived, TxPair(wtx, (CAccountingEntry*)0)));\n    }\n    std::list<CAccountingEntry> acentries;\n    walletdb.ListAccountCreditDebit("", acentries);\n    BOOST_FOREACH(CAccountingEntry& entry, acentries)\n    {\n        txByTime.insert(make_pair(entry.nTime, TxPair((CWalletTx*)0, &entry)));\n    }\n\n    nOrderPosNext = 0;\n    std::vector<int64_t> nOrderPosOffsets;\n    for (TxItems::iterator it = txByTime.begin(); it != txByTime.end(); ++it)\n    {\n        CWalletTx *const pwtx = (*it).second.first;\n        CAccountingEntry *const pacentry = (*it).second.second;\n        int64_t& nOrderPos = (pwtx != 0) ? pwtx->nOrderPos : pacentry->nOrderPos;\n\n        if (nOrderPos == -1)\n        {\n            nOrderPos = nOrderPosNext++;\n            nOrderPosOffsets.push_back(nOrderPos);\n\n            if (pwtx)\n            {\n                if (!walletdb.WriteTx(*pwtx))\n                    return DB_LOAD_FAIL;\n            }\n            else\n                if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry))\n                    return DB_LOAD_FAIL;\n        }\n        else\n        {\n            int64_t nOrderPosOff = 0;\n            BOOST_FOREACH(const int64_t& nOffsetStart, nOrderPosOffsets)\n            {\n                if (nOrderPos >= nOffsetStart)\n                    ++nOrderPosOff;\n            }\n            nOrderPos += nOrderPosOff;\n            nOrderPosNext = std::max(nOrderPosNext, nOrderPos + 1);\n\n            if (!nOrderPosOff)\n                continue;\n\n            // Since we\'re changing the order, write it back\n            if (pwtx)\n            {\n                if (!walletdb.WriteTx(*pwtx))\n                    return DB_LOAD_FAIL;\n            }\n            else\n                if (!walletdb.WriteAccountingEntry(pacentry->nEntryNo, *pacentry))\n                    return DB_LOAD_FAIL;\n        }\n    }\n    walletdb.WriteOrderPosNext(nOrderPosNext);\n\n    return DB_LOAD_OK;\n}\n\nint64_t CWallet::IncOrderPosNext(CWalletDB *pwalletdb)\n{\n    AssertLockHeld(cs_wallet); // nOrderPosNext\n    int64_t nRet = nOrderPosNext++;\n    if (pwalletdb) {\n        pwalletdb->WriteOrderPosNext(nOrderPosNext);\n    } else {\n        CWalletDB(strWalletFile).WriteOrderPosNext(nOrderPosNext);\n    }\n    return nRet;\n}\n\nbool CWallet::AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment)\n{\n    CWalletDB walletdb(strWalletFile);\n    if (!walletdb.TxnBegin())\n        return false;\n\n    int64_t nNow = GetAdjustedTime();\n\n    // Debit\n    CAccountingEntry debit;\n    debit.nOrderPos = IncOrderPosNext(&walletdb);\n    debit.strAccount = strFrom;\n    debit.nCreditDebit = -nAmount;\n    debit.nTime = nNow;\n    debit.strOtherAccount = strTo;\n    debit.strComment = strComment;\n    AddAccountingEntry(debit, &walletdb);\n\n    // Credit\n    CAccountingEntry credit;\n    credit.nOrderPos = IncOrderPosNext(&walletdb);\n    credit.strAccount = strTo;\n    credit.nCreditDebit = nAmount;\n    credit.nTime = nNow;\n    credit.strOtherAccount = strFrom;\n    credit.strComment = strComment;\n    AddAccountingEntry(credit, &walletdb);\n\n    if (!walletdb.TxnCommit())\n        return false;\n\n    return true;\n}\n\nbool CWallet::GetAccountPubkey(CPubKey &pubKey, std::string strAccount, bool bForceNew)\n{\n    CWalletDB walletdb(strWalletFile);\n\n    CAccount account;\n    walletdb.ReadAccount(strAccount, account);\n\n    if (!bForceNew) {\n        if (!account.vchPubKey.IsValid())\n            bForceNew = true;\n        else {\n            // Check if the current key has been used\n            CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID());\n            for (std::map<uint256, CWalletTx>::iterator it = mapWallet.begin();\n                 it != mapWallet.end() && account.vchPubKey.IsValid();\n                 ++it)\n                BOOST_FOREACH(const CTxOut& txout, (*it).second.tx->vout)\n                    if (txout.scriptPubKey == scriptPubKey) {\n                        bForceNew = true;\n                        break;\n                    }\n        }\n    }\n\n    // Generate a new key\n    if (bForceNew) {\n        if (!GetKeyFromPool(account.vchPubKey))\n            return false;\n\n        SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive");\n        walletdb.WriteAccount(strAccount, account);\n    }\n\n    pubKey = account.vchPubKey;\n\n    return true;\n}\n\nvoid CWallet::MarkDirty()\n{\n    {\n        LOCK(cs_wallet);\n        BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)\n            item.second.MarkDirty();\n    }\n}\n\nnamespace {\nvoid HandleSecretAddresses(CWallet & wallet, bip47::CAccountReceiver const & receiver)\n{\n    if (wallet.IsLocked()) {\n        wallet.NotifyBip47KeysChanged(receiver.getAccountNum(), chainActive.Tip());\n        return;\n    }\n\n    bip47::utils::AddReceiverSecretAddresses(receiver, wallet);\n}\n}\n\nbool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)\n{\n    LOCK(cs_wallet);\n\n    auto mi = mapWallet.find(originalHash);\n\n    // There is a bug if MarkReplaced is not called on an existing wallet transaction.\n    assert(mi != mapWallet.end());\n\n    CWalletTx& wtx = (*mi).second;\n\n    // Ensure for now that we\'re not overwriting data\n    assert(wtx.mapValue.count("replaced_by_txid") == 0);\n\n    wtx.mapValue["replaced_by_txid"] = newHash.ToString();\n\n    CWalletDB walletdb(strWalletFile, "r+");\n\n    bool success = true;\n    if (!walletdb.WriteTx(wtx)) {\n        LogPrintf("%s: Updating walletdb tx %s failed", __func__, wtx.GetHash().ToString());\n        success = false;\n    }\n\n    NotifyTransactionChanged(this, originalHash, CT_UPDATED);\n\n    return success;\n}\n\nbool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)\n{\n    LOCK(cs_wallet);\n\n    CWalletDB walletdb(strWalletFile, "r+", fFlushOnClose);\n\n    uint256 hash = wtxIn.GetHash();\n\n    // Inserts only if not already there, returns tx inserted or tx found\n    std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn));\n    CWalletTx& wtx = (*ret.first).second;\n    wtx.BindWallet(this);\n    bool fInsertedNew = ret.second;\n    if (fInsertedNew)\n    {\n        wtx.nTimeReceived = GetAdjustedTime();\n        wtx.nOrderPos = IncOrderPosNext(&walletdb);\n        wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));\n\n        auto mnList = deterministicMNManager->GetListAtChainTip();\n        for(unsigned int i = 0; i < wtx.tx->vout.size(); ++i) {\n            if (IsMine(wtx.tx->vout[i]) && !IsSpent(hash, i)) {\n                setWalletUTXO.insert(COutPoint(hash, i));\n                if (deterministicMNManager->IsProTxWithCollateral(wtx.tx, i) || mnList.HasMNByCollateral(COutPoint(hash, i))) {\n                    LockCoin(COutPoint(hash, i));\n                }\n            }\n        }\n\n        wtx.nTimeSmart = wtx.nTimeReceived;\n        if (!wtxIn.hashUnset())\n        {\n            if (mapBlockIndex.count(wtxIn.hashBlock))\n            {\n                int64_t latestNow = wtx.nTimeReceived;\n                int64_t latestEntry = 0;\n                {\n                    // Tolerate times up to the last timestamp in the wallet not more than 5 minutes into the future\n                    int64_t latestTolerated = latestNow + 300;\n                    const TxItems & txOrdered = wtxOrdered;\n                    for (TxItems::const_reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)\n                    {\n                        CWalletTx *const pwtx = (*it).second.first;\n                        if (pwtx == &wtx)\n                            continue;\n                        CAccountingEntry *const pacentry = (*it).second.second;\n                        int64_t nSmartTime;\n                        if (pwtx)\n                        {\n                            nSmartTime = pwtx->nTimeSmart;\n                            if (!nSmartTime)\n                                nSmartTime = pwtx->nTimeReceived;\n                        }\n                        else\n                            nSmartTime = pacentry->nTime;\n                        if (nSmartTime <= latestTolerated)\n                        {\n                            latestEntry = nSmartTime;\n                            if (nSmartTime > latestNow)\n                                latestNow = nSmartTime;\n                            break;\n                        }\n                    }\n                }\n\n                int64_t blocktime = mapBlockIndex[wtxIn.hashBlock]->GetBlockTime();\n                wtx.nTimeSmart = std::max(latestEntry, std::min(blocktime, latestNow));\n            }\n            else\n                LogPrintf("AddToWallet(): found %s in block %s not in index\\n",\n                         wtxIn.GetHash().ToString(),\n                         wtxIn.hashBlock.ToString());\n        }\n        AddToSpends(hash);\n    }\n\n    bool fUpdated = false;\n    if (!fInsertedNew)\n    {\n        // Merge\n        if (!wtxIn.hashUnset() && wtxIn.hashBlock != wtx.hashBlock)\n        {\n            wtx.hashBlock = wtxIn.hashBlock;\n            fUpdated = true;\n        }\n        // If no longer abandoned, update\n        if (wtxIn.hashBlock.IsNull() && wtx.isAbandoned())\n        {\n            wtx.hashBlock = wtxIn.hashBlock;\n            fUpdated = true;\n        }\n        if (wtxIn.nIndex != -1 && (wtxIn.nIndex != wtx.nIndex))\n        {\n            wtx.nIndex = wtxIn.nIndex;\n            fUpdated = true;\n        }\n        if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe)\n        {\n            wtx.fFromMe = wtxIn.fFromMe;\n            fUpdated = true;\n        }\n    }\n\n    //// debug print\n    LogPrintf("AddToWallet %s  %s%s\\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : ""));\n\n    // Write to disk\n    if (fInsertedNew || fUpdated)\n        if (!walletdb.WriteTx(wtx))\n            return false;\n\n    // Handle bip47 notification tx\n    if (fInsertedNew)\n    {\n        HandleBip47Transaction(wtx);\n    }\n\n    // Break debit/credit balance caches:\n    wtx.MarkDirty();\n\n    // Notify UI of new or updated transaction\n    NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);\n\n    // notify an external script when a wallet transaction comes in or is updated\n    std::string strCmd = GetArg("-walletnotify", "");\n\n    if ( !strCmd.empty())\n    {\n        boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex());\n        boost::thread t(runCommand, strCmd); // thread runs free\n    }\n\n    return true;\n}\n\nbool CWallet::LoadToWallet(const CWalletTx& wtxIn)\n{\n    uint256 hash = wtxIn.GetHash();\n\n    mapWallet[hash] = wtxIn;\n    CWalletTx& wtx = mapWallet[hash];\n    wtx.BindWallet(this);\n    wtxOrdered.insert(make_pair(wtx.nOrderPos, TxPair(&wtx, (CAccountingEntry*)0)));\n    AddToSpends(hash);\n    BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin) {\n        if (mapWallet.count(txin.prevout.hash)) {\n            CWalletTx& prevtx = mapWallet[txin.prevout.hash];\n            if (prevtx.nIndex == -1 && !prevtx.hashUnset()) {\n                MarkConflicted(prevtx.hashBlock, wtx.GetHash());\n            }\n        }\n    }\n\n    return true;\n}\n\n/**\n * Add a transaction to the wallet, or update it.  pIndex and posInBlock should\n * be set when the transaction was known to be included in a block.  When\n * posInBlock = SYNC_TRANSACTION_NOT_IN_BLOCK (-1) , then wallet state is not\n * updated in AddToWallet, but notifications happen and cached balances are\n * marked dirty.\n * If fUpdate is true, existing transactions will be updated.\n * TODO: One exception to this is that the abandoned state is cleared under the\n * assumption that any further notification of a transaction that was considered\n * abandoned is an indication that it is not safe to be considered abandoned.\n * Abandoned state should probably be more carefuly tracked via different\n * posInBlock signals or by checking mempool presence when necessary.\n */\nbool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate)\n{\n    {\n        AssertLockHeld(cs_wallet);\n\n        if (posInBlock != -1) {\n            if(!(tx.IsCoinBase() || tx.IsSigmaSpend() || tx.IsZerocoinRemint() || tx.IsZerocoinSpend()) || tx.IsLelantusJoinSplit()) {\n                BOOST_FOREACH(const CTxIn& txin, tx.vin) {\n                    std::pair<TxSpends::const_iterator, TxSpends::const_iterator> range = mapTxSpends.equal_range(txin.prevout);\n                    while (range.first != range.second) {\n                        if (range.first->second != tx.GetHash()) {\n                            LogPrintf("Transaction %s (in block %s) conflicts with wallet transaction %s (both spend %s:%i)\\n", tx.GetHash().ToString(), pIndex->GetBlockHash().ToString(), range.first->second.ToString(), range.first->first.hash.ToString(), range.first->first.n);\n                            MarkConflicted(pIndex->GetBlockHash(), range.first->second);\n                        }\n                        range.first++;\n                    }\n                }\n            }\n        }\n\n        bool fExisted = mapWallet.count(tx.GetHash()) != 0;\n        if (fExisted && !fUpdate) return false;\n        if (fExisted || IsMine(tx) || IsFromMe(tx))\n        {\n            /* Check if any keys in the wallet keypool that were supposed to be unused\n         * have appeared in a new transaction. If so, remove those keys from the keypool.\n         * This can happen when restoring an old wallet backup that does not contain\n         * the mostly recently created transactions from newer versions of the wallet.\n         */\n            // loop though all outputs\n            for (const CTxOut& txout: tx.vout) {\n                // extract addresses and check if they match with an unused keypool key\n                std::vector<CKeyID> vAffected;\n                CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);\n                for (const CKeyID &keyid : vAffected) {\n                    std::map<CKeyID, int64_t>::const_iterator mi = m_pool_key_to_index.find(keyid);\n                    if (mi != m_pool_key_to_index.end()) {\n                        LogPrintf("%s: Detected a used keypool key, mark all keypool key up to this key as used\\n", __func__);\n                        MarkReserveKeysAsUsed(mi->second);\n\n                        if (!TopUpKeyPool()) {\n                            LogPrintf("%s: Topping up keypool failed (locked wallet)\\n", __func__);\n                        }\n                    }\n                }\n            }\n\n            CWalletTx wtx(this, MakeTransactionRef(tx));\n\n            // Get merkle branch if transaction was found in a block\n            if (posInBlock != -1)\n                wtx.SetMerkleBranch(pIndex, posInBlock);\n\n            return AddToWallet(wtx, false);\n        }\n    }\n    return false;\n}\n\nbool CWallet::AbandonTransaction(const uint256& hashTx)\n{\n    LOCK2(cs_main, cs_wallet);\n\n    CWalletDB walletdb(strWalletFile, "r+");\n\n    std::set<uint256> todo;\n    std::set<uint256> done;\n\n    // Can\'t mark abandoned if confirmed or in mempool\n    assert(mapWallet.count(hashTx));\n    CWalletTx& origtx = mapWallet[hashTx];\n    if (origtx.GetDepthInMainChain() > 0 || origtx.InMempool() || origtx.InStempool() || origtx.IsLockedByLLMQInstantSend()) {\n        return false;\n    }\n\n    todo.insert(hashTx);\n\n    while (!todo.empty()) {\n        uint256 now = *todo.begin();\n        todo.erase(now);\n        done.insert(now);\n        assert(mapWallet.count(now));\n        CWalletTx& wtx = mapWallet[now];\n        int currentconfirm = wtx.GetDepthInMainChain();\n        // If the orig tx was not in block, none of its spends can be\n        assert(currentconfirm <= 0);\n        // if (currentconfirm < 0) {Tx and spends are already conflicted, no need to abandon}\n        if (currentconfirm == 0 && !wtx.isAbandoned()) {\n            // If the orig tx was not in block/mempool, none of its spends can be in mempool\n            assert(!wtx.InMempool());\n            assert(!wtx.InStempool());\n            wtx.nIndex = -1;\n            wtx.setAbandoned();\n            wtx.MarkDirty();\n            walletdb.WriteTx(wtx);\n            NotifyTransactionChanged(this, wtx.GetHash(), CT_UPDATED);\n            // Iterate over all its outputs, and mark transactions in the wallet that spend them abandoned too\n            TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(hashTx, 0));\n            while (iter != mapTxSpends.end() && iter->first.hash == now) {\n                if (!done.count(iter->second)) {\n                    todo.insert(iter->second);\n                }\n                iter++;\n            }\n            // If a transaction changes \'conflicted\' state, that changes the balance\n            // available of the outputs it spends. So force those to be recomputed\n            BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)\n            {\n                if (mapWallet.count(txin.prevout.hash))\n                    mapWallet[txin.prevout.hash].MarkDirty();\n            }\n        }\n\n        if (wtx.tx->IsSigmaSpend()) {\n            // find out coin serial number\n            assert(wtx.tx->vin.size() == 1);\n\n            const CTxIn &txin = wtx.tx->vin[0];\n            // NOTE(martun): +1 on the next line stands for 1 byte in which the opcode of\n            // OP_SIGMASPEND is written.\n            // because the size of serialized spend is also written, probably in 3 bytes.\n            CDataStream serializedCoinSpend((const char *)&*(txin.scriptSig.begin() + 1),\n                                            (const char *)&*txin.scriptSig.end(),\n                                            SER_NETWORK, PROTOCOL_VERSION);\n            sigma::CoinSpend spend(sigma::Params::get_default(),\n                                         serializedCoinSpend);\n\n            Scalar serial = spend.getCoinSerialNumber();\n\n            // mark corresponding mint as unspent\n            uint256 hashSerial = primitives::GetSerialHash(serial);\n            CMintMeta meta;\n            if(zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta)){\n                meta.isUsed = false;\n                zwallet->GetTracker().UpdateState(meta);\n\n                // erase sigma spend entry\n                CSigmaSpendEntry spendEntry;\n                spendEntry.coinSerial = serial;\n                walletdb.EraseCoinSpendSerialEntry(spendEntry);\n            }\n        } else if (wtx.tx->IsLelantusJoinSplit()) {\n            // find out coin serial number\n            assert(wtx.tx->vin.size() == 1);\n\n            std::unique_ptr<lelantus::JoinSplit> joinsplit;\n            try {\n                joinsplit = lelantus::ParseLelantusJoinSplit(*wtx.tx);\n            }\n            catch (...) {\n                continue;\n            }\n\n            const std::vector<Scalar>& serials = joinsplit->getCoinSerialNumbers();\n\n            for (const auto& serial : serials) {\n                // mark corresponding mint as unspent\n                uint256 hashSerial = primitives::GetSerialHash(serial);\n                CLelantusMintMeta meta;\n                if(zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta)){\n                    meta.isUsed = false;\n                    zwallet->GetTracker().UpdateState(meta);\n\n                    // erase lelantus spend entry\n                    CLelantusSpendEntry spendEntry;\n                    spendEntry.coinSerial = serial;\n                    walletdb.EraseLelantusSpendSerialEntry(spendEntry);\n                }\n            }\n        }\n\n        if (wtx.tx->IsSigmaMint()) {\n            for (const CTxOut &txout: wtx.tx->vout) {\n                if (!txout.scriptPubKey.IsSigmaMint())\n                    continue;\n\n                try {\n                    auto groupElement = sigma::ParseSigmaMintScript(txout.scriptPubKey);\n                    uint256 hashPubcoin = primitives::GetPubCoinValueHash(groupElement);\n                    CMintMeta meta;\n                    if (zwallet->GetTracker().GetMetaFromPubcoin(hashPubcoin, meta))\n                        zwallet->GetTracker().Archive(meta);\n                }\n                catch (std::invalid_argument &) {\n                    continue;\n                }\n            }\n        }\n\n        if (wtx.tx->IsLelantusMint()) {\n            for (const CTxOut &txout: wtx.tx->vout) {\n                if (!txout.scriptPubKey.IsLelantusMint() && !txout.scriptPubKey.IsLelantusJMint())\n                    continue;\n\n                try {\n                    secp_primitives::GroupElement groupElement;\n                    lelantus::ParseLelantusMintScript(txout.scriptPubKey, groupElement);\n                    uint256 hashPubcoin = primitives::GetPubCoinValueHash(groupElement);\n                    CLelantusMintMeta meta;\n                    if (zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, meta))\n                        zwallet->GetTracker().Archive(meta);\n                }\n                catch (std::invalid_argument &) {\n                    continue;\n                }\n            }\n        }\n    }\n\n    return true;\n}\n\nvoid CWallet::MarkConflicted(const uint256& hashBlock, const uint256& hashTx)\n{\n    LOCK2(cs_main, cs_wallet);\n\n    int conflictconfirms = 0;\n    if (mapBlockIndex.count(hashBlock)) {\n        CBlockIndex* pindex = mapBlockIndex[hashBlock];\n        if (chainActive.Contains(pindex)) {\n            conflictconfirms = -(chainActive.Height() - pindex->nHeight + 1);\n        }\n    }\n    // If number of conflict confirms cannot be determined, this means\n    // that the block is still unknown or not yet part of the main chain,\n    // for example when loading the wallet during a reindex. Do nothing in that\n    // case.\n    if (conflictconfirms >= 0)\n        return;\n\n    // Do not flush the wallet here for performance reasons\n    CWalletDB walletdb(strWalletFile, "r+", false);\n\n    std::set<uint256> todo;\n    std::set<uint256> done;\n\n    todo.insert(hashTx);\n\n    while (!todo.empty()) {\n        uint256 now = *todo.begin();\n        todo.erase(now);\n        done.insert(now);\n        assert(mapWallet.count(now));\n        CWalletTx& wtx = mapWallet[now];\n        int currentconfirm = wtx.GetDepthInMainChain();\n        if (conflictconfirms < currentconfirm) {\n            // Block is \'more conflicted\' than current confirm; update.\n            // Mark transaction as conflicted with this block.\n            wtx.nIndex = -1;\n            wtx.hashBlock = hashBlock;\n            wtx.MarkDirty();\n            walletdb.WriteTx(wtx);\n            // Iterate over all its outputs, and mark transactions in the wallet that spend them conflicted too\n            TxSpends::const_iterator iter = mapTxSpends.lower_bound(COutPoint(now, 0));\n            while (iter != mapTxSpends.end() && iter->first.hash == now) {\n                 if (!done.count(iter->second)) {\n                     todo.insert(iter->second);\n                 }\n                 iter++;\n            }\n            // If a transaction changes \'conflicted\' state, that changes the balance\n            // available of the outputs it spends. So force those to be recomputed\n            BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)\n            {\n                if (mapWallet.count(txin.prevout.hash))\n                    mapWallet[txin.prevout.hash].MarkDirty();\n            }\n        }\n    }\n}\n\nvoid CWallet::SyncTransaction(const CTransaction& tx, const CBlockIndex *pindex, int posInBlock)\n{\n    LOCK2(cs_main, cs_wallet);\n\n    if (!AddToWalletIfInvolvingMe(tx, pindex, posInBlock, true))\n        return; // Not one of ours\n\n    // If a transaction changes \'conflicted\' state, that changes the balance\n    // available of the outputs it spends. So force those to be\n    // recomputed, also:\n    BOOST_FOREACH(const CTxIn& txin, tx.vin)\n    {\n        if (mapWallet.count(txin.prevout.hash))\n            mapWallet[txin.prevout.hash].MarkDirty();\n    }\n}\n\n\nisminetype CWallet::IsMine(const CTxIn &txin, const CTransaction& tx) const\n{\n    LOCK(cs_wallet);\n\n    if (txin.IsZerocoinSpend()) {\n        return ISMINE_NO;\n    } else if (txin.IsSigmaSpend()) {\n        CWalletDB db(strWalletFile);\n\n        CDataStream serializedCoinSpend(\n            std::vector<char>(txin.scriptSig.begin() + 1, txin.scriptSig.end()),\n            SER_NETWORK, PROTOCOL_VERSION);\n\n        sigma::Params* sigmaParams = sigma::Params::get_default();\n        sigma::CoinSpend spend(sigmaParams, serializedCoinSpend);\n\n        if (db.HasCoinSpendSerialEntry(spend.getCoinSerialNumber())) {\n            return ISMINE_SPENDABLE;\n        }\n    } else if (txin.IsLelantusJoinSplit()) {\n        CWalletDB db(strWalletFile);\n        std::unique_ptr<lelantus::JoinSplit> joinsplit;\n        try {\n            joinsplit = lelantus::ParseLelantusJoinSplit(tx);\n        }\n        catch (...) {\n            return ISMINE_NO;\n        }\n\n        if (db.HasLelantusSpendSerialEntry(joinsplit->getCoinSerialNumbers()[0]) || db.HasCoinSpendSerialEntry(joinsplit->getCoinSerialNumbers()[0])) {\n            return ISMINE_SPENDABLE;\n        }\n    } else if (txin.IsZerocoinRemint()) {\n        return ISMINE_NO;\n    }\n    else {\n        std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);\n        if (mi != mapWallet.end())\n        {\n            const CWalletTx& prev = (*mi).second;\n            if (txin.prevout.n < prev.tx->vout.size())\n                return IsMine(prev.tx->vout[txin.prevout.n]);\n        }\n    }\n\n    return ISMINE_NO;\n}\n\n// Note that this function doesn\'t distinguish between a 0-valued input,\n// and a not-"is mine" (according to the filter) input.\nCAmount CWallet::GetDebit(const CTxIn &txin, const CTransaction& tx, const isminefilter& filter) const\n{\n    LOCK(cs_wallet);\n\n    if (txin.IsZerocoinSpend()) {\n        // Reverting it to its pre-Sigma state.\n        goto end;\n    } else if (txin.IsSigmaSpend()) {\n        if (!(filter & ISMINE_SPENDABLE)) {\n            goto end;\n        }\n\n        CWalletDB db(strWalletFile);\n        std::unique_ptr<sigma::CoinSpend> spend;\n\n        try {\n            std::tie(spend, std::ignore) = sigma::ParseSigmaSpend(txin);\n        } catch (CBadTxIn&) {\n            goto end;\n        }\n\n        if (db.HasCoinSpendSerialEntry(spend->getCoinSerialNumber())) {\n            return spend->getIntDenomination();\n        }\n    } else if (txin.IsZerocoinRemint()) {\n        return 0;\n    } else if (txin.IsLelantusJoinSplit()) {\n        if (!(filter & ISMINE_SPENDABLE)) {\n            goto end;\n        }\n\n        CWalletDB db(strWalletFile);\n        std::unique_ptr<lelantus::JoinSplit> joinsplit;\n        try {\n            joinsplit = lelantus::ParseLelantusJoinSplit(tx);\n        }\n        catch (...) {\n            goto end;\n        }\n\n        CAmount amount = 0;\n\n        const std::vector<Scalar>& serials = joinsplit->getCoinSerialNumbers();\n        for (const auto& serial : serials) {\n            CLelantusSpendEntry lelantusSpend;\n            if(db.ReadLelantusSpendSerialEntry(serial, lelantusSpend))\n                amount += lelantusSpend.amount;\n        }\n        return amount;\n    } else {\n        std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash);\n        if (mi != mapWallet.end())\n        {\n            const CWalletTx& prev = (*mi).second;\n            if (txin.prevout.n < prev.tx->vout.size())\n                if (IsMine(prev.tx->vout[txin.prevout.n]) & filter)\n                    return prev.tx->vout[txin.prevout.n].nValue;\n        }\n    }\n\nend:\n    return 0;\n}\n\nisminetype CWallet::IsMine(const CTxOut &txout) const\n{\n    LOCK(cs_wallet);\n\n    if (txout.scriptPubKey.IsSigmaMint() || txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint()) {\n        CWalletDB db(strWalletFile);\n        secp_primitives::GroupElement pub;\n\n            if (txout.scriptPubKey.IsSigmaMint()) {\n                try {\n                    pub = sigma::ParseSigmaMintScript(txout.scriptPubKey);\n                } catch (std::invalid_argument &) {\n                    return ISMINE_NO;\n                }\n\n            }\n            else {\n                try {\n                    lelantus::ParseLelantusMintScript(txout.scriptPubKey, pub);\n                } catch (std::invalid_argument &) {\n                    return ISMINE_NO;\n                }\n            }\n        return db.HasHDMint(pub) ? ISMINE_SPENDABLE : ISMINE_NO;\n    } else {\n        return ::IsMine(*this, txout.scriptPubKey);\n    }\n}\n\nCAmount CWallet::GetCredit(const CTxOut& txout, const isminefilter& filter) const\n{\n    if (!MoneyRange(txout.nValue))\n        throw std::runtime_error(std::string(__func__) + ": value out of range");\n    if (txout.scriptPubKey.IsLelantusJMint()) {\n        if (!(filter & ISMINE_SPENDABLE))\n            return 0;\n        CWalletDB db(strWalletFile);\n        secp_primitives::GroupElement pub;\n        try {\n            std::vector<unsigned char> encryptedValue;\n            lelantus::ParseLelantusJMintScript(txout.scriptPubKey, pub, encryptedValue);\n        } catch (std::invalid_argument&) {\n            return ISMINE_NO;\n        }\n        uint256 hashPubcoin = primitives::GetPubCoinValueHash(pub);\n        CHDMint dMint;\n        if (db.ReadHDMint(hashPubcoin, true, dMint)) {\n            return dMint.GetAmount();\n        }\n        return 0;\n    }\n    return ((IsMine(txout) & filter) ? txout.nValue : 0);\n}\n\nbool CWallet::IsChange(const uint256& tx, const CTxOut &txout) const\n{\n    auto wtx = GetWalletTx(tx);\n    if (!wtx) {\n        throw std::invalid_argument("The specified transaction hash is not belong to the wallet");\n    }\n\n    return wtx->IsChange(txout);\n}\n\nCAmount CWallet::GetChange(const uint256& tx, const CTxOut &txout) const\n{\n    if (!MoneyRange(txout.nValue))\n        throw std::runtime_error(std::string(__func__) + ": value out of range");\n    return (IsChange(tx, txout) ? txout.nValue : 0);\n}\n\nbool CWallet::IsMine(const CTransaction& tx) const\n{\n    BOOST_FOREACH(const CTxOut& txout, tx.vout)\n        if (IsMine(txout))\n            return true;\n    return false;\n}\n\nbool CWallet::IsFromMe(const CTransaction& tx) const\n{\n    return (GetDebit(tx, ISMINE_ALL) > 0);\n}\n\nCAmount CWallet::GetDebit(const CTransaction& tx, const isminefilter& filter) const\n{\n    CAmount nDebit = 0;\n    BOOST_FOREACH(const CTxIn& txin, tx.vin)\n    {\n        nDebit += GetDebit(txin, tx, filter);\n        if (!MoneyRange(nDebit))\n            throw std::runtime_error(std::string(__func__) + ": value out of range");\n    }\n    return nDebit;\n}\n\nbool CWallet::IsAllFromMe(const CTransaction& tx, const isminefilter& filter) const\n{\n    LOCK(cs_wallet);\n\n    BOOST_FOREACH(const CTxIn& txin, tx.vin)\n    {\n        auto mi = mapWallet.find(txin.prevout.hash);\n        if (mi == mapWallet.end())\n            return false; // any unknown inputs can\'t be from us\n\n        const CWalletTx& prev = (*mi).second;\n\n        if (txin.prevout.n >= prev.tx->vout.size())\n            return false; // invalid input!\n\n        if (!(IsMine(prev.tx->vout[txin.prevout.n]) & filter))\n            return false;\n    }\n    return true;\n}\n\nCAmount CWallet::GetCredit(const CTransaction& tx, const isminefilter& filter) const\n{\n    CAmount nCredit = 0;\n    BOOST_FOREACH(const CTxOut& txout, tx.vout)\n    {\n        nCredit += GetCredit(txout, filter);\n        if (!MoneyRange(nCredit))\n            throw std::runtime_error(std::string(__func__) + ": value out of range");\n    }\n    return nCredit;\n}\n\nCAmount CWallet::GetChange(const CTransaction& tx) const\n{\n    CAmount nChange = 0;\n    BOOST_FOREACH(const CTxOut& txout, tx.vout)\n    {\n        nChange += GetChange(tx.GetHash(), txout);\n        if (!MoneyRange(nChange))\n            throw std::runtime_error(std::string(__func__) + ": value out of range");\n    }\n    return nChange;\n}\n\nCPubKey CWallet::GenerateNewHDMasterKey()\n{\n    CKey key;\n    key.MakeNewKey(true);\n\n    int64_t nCreationTime = GetTime();\n    CKeyMetadata metadata(nCreationTime);\n\n    // calculate the pubkey\n    CPubKey pubkey = key.GetPubKey();\n    assert(key.VerifyPubKey(pubkey));\n\n    // set the hd keypath to "m" -> Master, refers the masterkeyid to itself\n    metadata.hdKeypath     = "m";\n    metadata.hdMasterKeyID = pubkey.GetID();\n\n    {\n        LOCK(cs_wallet);\n\n        // mem store the metadata\n        mapKeyMetadata[pubkey.GetID()] = metadata;\n\n        // write the key&metadata to the database\n        if (!AddKeyPubKey(key, pubkey))\n            throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed");\n    }\n\n    return pubkey;\n}\n\nvoid CWallet::GenerateNewMnemonic()\n{\n    CHDChain newHdChain;\n    MnemonicContainer mnContainer;\n\n    std::string strSeed = GetArg("-hdseed", "not hex");\n\n    bool isHDSeedSet = strSeed != "not hex";\n\n    if(isHDSeedSet && IsHex(strSeed)) {\n        std::vector<unsigned char> seed = ParseHex(strSeed);\n        if (!mnContainer.SetSeed(SecureVector(seed.begin(), seed.end())))\n            throw std::runtime_error(std::string(__func__) + ": SetSeed failed");\n        newHdChain.masterKeyID = CKeyID(Hash160(seed.begin(), seed.end()));\n    }\n    else {\n        LogPrintf("CWallet::GenerateNewMnemonic -- Generating new MnemonicContainer\\n");\n\n        std::string mnemonic = GetArg("-mnemonic", "");\n        std::string mnemonicPassphrase = GetArg("-mnemonicpassphrase", "");\n        //remove trailing string identifiers\n        boost::algorithm::trim_if(mnemonic, [](char c){return c==\'\\"\' || c==\'\\\'\';});\n        boost::algorithm::trim_if(mnemonicPassphrase, [](char c){return c==\'\\"\' || c==\'\\\'\';});\n        //Use 24 words by default;\n        bool use12Words = GetBoolArg("-use12", false);\n        mnContainer.Set12Words(use12Words);\n\n        SecureString secureMnemonic(mnemonic.begin(), mnemonic.end());\n        SecureString securePassphrase(mnemonicPassphrase.begin(), mnemonicPassphrase.end());\n\n        if (!mnContainer.SetMnemonic(secureMnemonic, securePassphrase))\n            throw std::runtime_error(std::string(__func__) + ": SetMnemonic failed");\n        newHdChain.masterKeyID = CKeyID(Hash160(mnContainer.seed.begin(), mnContainer.seed.end()));\n    }\n\n    if (!SetHDChain(newHdChain, false))\n        throw std::runtime_error(std::string(__func__) + ": SetHDChain failed");\n\n    if (!SetMnemonicContainer(mnContainer, false))\n        throw std::runtime_error(std::string(__func__) + ": SetMnemonicContainer failed");\n}\n\nbool CWallet::SetHDMasterKey(const CPubKey &pubkey, const int cHDChainVersion) {\n    LOCK(cs_wallet);\n\n    // ensure this wallet.dat can only be opened by clients supporting HD\n    SetMinVersion(FEATURE_HD);\n\n    // store the keyid (hash160) together with\n    // the child index counter in the database\n    // as a hdchain object\n    CHDChain newHdChain;\n    newHdChain.nVersion = cHDChainVersion;\n    newHdChain.masterKeyID = pubkey.GetID();\n    SetHDChain(newHdChain, false);\n\n    return true;\n}\n\nbool CWallet::SetHDChain(const CHDChain &chain, bool memonly, bool& upgradeChain, bool genNewKeyPool)\n{\n    LOCK(cs_wallet);\n    upgradeChain = (chain.nVersion==CHDChain::VERSION_BASIC);\n    if (upgradeChain && !IsLocked()) { // Upgrade HDChain to latest version\n        CHDChain newChain;\n        newChain.masterKeyID = chain.masterKeyID;\n        newChain.nVersion = CHDChain::VERSION_WITH_BIP44; // old versions cannot use mnemonic\n        // whether to generate the keypool now (conditional as leads to DB deadlock if loading DB simultaneously)\n        if (genNewKeyPool)\n            NewKeyPool();\n        if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(newChain))\n            throw std::runtime_error(std::string(__func__) + ": writing chain failed");\n        hdChain = newChain;\n    }\n    else {\n        if (!memonly && !CWalletDB(strWalletFile).WriteHDChain(chain))\n            throw std::runtime_error(std::string(__func__) + ": writing chain failed");\n        hdChain = chain;\n    }\n\n    return true;\n}\n\nbool CWallet::IsHDEnabled()\n{\n    return !hdChain.masterKeyID.IsNull();\n}\n\nbool CWallet::SetMnemonicContainer(const MnemonicContainer& mnContainer, bool memonly) {\n    if (!memonly && !CWalletDB(strWalletFile).WriteMnemonic(mnContainer))\n        throw std::runtime_error(std::string(__func__) + ": writing chain failed");\n    mnemonicContainer = mnContainer;\n    return true;\n}\n\nbool CWallet::EncryptMnemonicContainer(const CKeyingMaterial& vMasterKeyIn)\n{\n    if (!IsCrypted())\n        return false;\n\n    if (mnemonicContainer.IsCrypted())\n        return true;\n\n    uint256 id = uint256S(hdChain.masterKeyID.GetHex());\n\n    std::vector<unsigned char> cryptedSeed;\n    if (!EncryptMnemonicSecret(vMasterKeyIn, mnemonicContainer.GetSeed(), id, cryptedSeed))\n        return false;\n    SecureVector secureCryptedSeed(cryptedSeed.begin(), cryptedSeed.end());\n    if (!mnemonicContainer.SetSeed(secureCryptedSeed))\n        return false;\n\n    SecureString mnemonic;\n    if (mnemonicContainer.GetMnemonic(mnemonic)) {\n        std::vector<unsigned char> cryptedMnemonic;\n        SecureVector vectorMnemonic(mnemonic.begin(), mnemonic.end());\n\n        if ((!mnemonic.empty() && !EncryptMnemonicSecret(vMasterKeyIn, vectorMnemonic, id, cryptedMnemonic)))\n            return false;\n\n        SecureVector secureCryptedMnemonic(cryptedMnemonic.begin(), cryptedMnemonic.end());\n        if (!mnemonicContainer.SetMnemonic(secureCryptedMnemonic))\n            return false;\n    }\n\n    mnemonicContainer.SetCrypted(true);\n\n    return true;\n}\n\nbool CWallet::DecryptMnemonicContainer(MnemonicContainer& mnContainer)\n{\n    if (!IsCrypted())\n        return true;\n\n    if (!mnemonicContainer.IsCrypted())\n        return false;\n\n    uint256 id = uint256S(hdChain.masterKeyID.GetHex());\n\n    SecureVector seed;\n    SecureVector cryptedSeed = mnemonicContainer.GetSeed();\n    std::vector<unsigned char> vCryptedSeed(cryptedSeed.begin(), cryptedSeed.end());\n    if (!DecryptMnemonicSecret(vCryptedSeed, id, seed))\n        return false;\n\n    mnContainer = mnemonicContainer;\n    if (!mnContainer.SetSeed(seed))\n        return false;\n\n    SecureString cryptedMnemonic;\n\n    if (mnemonicContainer.GetMnemonic(cryptedMnemonic)) {\n        SecureVector vectorMnemonic;\n\n        std::vector<unsigned char> CryptedMnemonic(cryptedMnemonic.begin(), cryptedMnemonic.end());\n        if (!CryptedMnemonic.empty() && !DecryptMnemonicSecret(CryptedMnemonic, id, vectorMnemonic))\n            return false;\n\n        if (!mnContainer.SetMnemonic(vectorMnemonic))\n            return false;\n    }\n\n    mnContainer.SetCrypted(false);\n\n    return true;\n}\n\nint64_t CWalletTx::GetTxTime() const {\n    int64_t n = nTimeSmart;\n    return n ? n : nTimeReceived;\n}\n\nint CWalletTx::GetRequestCount() const\n{\n    // Returns -1 if it wasn\'t being tracked\n    int nRequests = -1;\n    {\n        LOCK(pwallet->cs_wallet);\n        if (IsCoinBase())\n        {\n            // Generated block\n            if (!hashUnset())\n            {\n                std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(hashBlock);\n                if (mi != pwallet->mapRequestCount.end())\n                    nRequests = (*mi).second;\n            }\n        }\n        else\n        {\n            // Did anyone request this transaction?\n            std::map<uint256, int>::const_iterator mi = pwallet->mapRequestCount.find(GetHash());\n            if (mi != pwallet->mapRequestCount.end())\n            {\n                nRequests = (*mi).second;\n\n                // How about the block it\'s in?\n                if (nRequests == 0 && !hashUnset())\n                {\n                    std::map<uint256, int>::const_iterator _mi = pwallet->mapRequestCount.find(hashBlock);\n                    if (_mi != pwallet->mapRequestCount.end())\n                        nRequests = (*_mi).second;\n                    else\n                        nRequests = 1; // If it\'s in someone else\'s block it must have got out\n                }\n            }\n        }\n    }\n    return nRequests;\n}\n\nvoid CWalletTx::GetAmounts(std::list<COutputEntry>& listReceived,\n                           std::list<COutputEntry>& listSent, CAmount& nFee, std::string& strSentAccount, const isminefilter& filter) const\n{\n    nFee = 0;\n    listReceived.clear();\n    listSent.clear();\n    strSentAccount = strFromAccount;\n\n    // Compute fee:\n    CAmount nDebit = GetDebit(filter);\n    if (nDebit > 0) // debit>0 means we signed/sent this transaction\n    {\n        if (!tx->IsLelantusJoinSplit()) {\n            CAmount nValueOut = tx->GetValueOut();\n            nFee = nDebit - nValueOut;\n        }\n        else\n            try {\n                nFee = lelantus::ParseLelantusJoinSplit(*tx)->getFee();\n            }\n            catch (...) {\n                // do nothing\n            }\n    }\n\n    // Sent/received.\n    for (unsigned int i = 0; i < tx->vout.size(); ++i)\n    {\n        const CTxOut& txout = tx->vout[i];\n        isminetype fIsMine = pwallet->IsMine(txout);\n        // Only need to handle txouts if AT LEAST one of these is true:\n        //   1) they debit from us (sent)\n        //   2) the output is to us (received)\n        if (nDebit > 0)\n        {\n            // Don\'t report \'change\' txouts\n            if (IsChange(static_cast<uint32_t>(i)))\n                continue;\n                //    GetHash().ToString(), (nEmbargo - nCurrTime) / 1000000);\n                CInv inv(MSG_DANDELION_TX, GetHash());\n                return CNode::localDandelionDestinationPushInventory(inv);\n            } else {\n                // LogPrintf("Relaying wtx %s\\n", GetHash().ToString());\n                g_connman->RelayTransaction(*this);\n                return true;\n            }\n        }\n    }\n            LogPrintf("CWalletTx::GetAmounts: Unknown transaction type found, txid %s\\n",\n                     this->GetHash().ToString());\n            address = CNoDestination();\n        }\n\n        CAmount nValue;\n        if(txout.scriptPubKey.IsLelantusJMint()) {\n            LOCK(pwalletMain->cs_wallet);\n            nValue = pwallet->GetCredit(txout, ISMINE_SPENDABLE);\n        } else {\n            nValue = txout.nValue;\n        }\n\n        COutputEntry output = {address, nValue, (int)i};\n\n        // If we are debited by the transaction, add the output as a "sent" entry\n        if (nDebit > 0)\n            listSent.push_back(output);\n\n        // If we are receiving the output, add it as a "received" entry\n        if (fIsMine & filter)\n            listReceived.push_back(output);\n    }\n\n}\n\n/**\n * Scan the block chain (starting in pindexStart) for transactions\n * from or to us. If fUpdate is true, found transactions that already\n * exist in the wallet will be updated.\n *\n * Returns pointer to the first block in the last contiguous range that was\n * successfully scanned.\n *\n */\nCBlockIndex* CWallet::ScanForWalletTransactions(CBlockIndex *pindexStart, bool fUpdate, bool fRecoverMnemonic)\n{\n    CBlockIndex* ret = nullptr;\n    int64_t nNow = GetTime();\n    const CChainParams& chainParams = Params();\n\n    CBlockIndex* pindex = pindexStart;\n    {\n        LOCK2(cs_main, cs_wallet);\n\n        // no need to read and scan block, if block was created before\n        // our wallet birthday (as adjusted for block time variability)\n        // if you are recovering wallet with mnemonics start rescan from block when mnemonics implemented in Firo\n        if (fRecoverMnemonic) {\n            pindex = chainActive[chainParams.GetConsensus().nMnemonicBlock];\n            if (pindex == NULL)\n                pindex = chainActive.Tip();\n        } else\n            while (pindex && nTimeFirstKey && (pindex->GetBlockTime() < (nTimeFirstKey - 7200)))\n                pindex = chainActive.Next(pindex);\n\n        ShowProgress(_("Rescanning..."), 0); // show rescan progress in GUI as dialog or on splashscreen, if -rescan on startup\n        double dProgressStart = GuessVerificationProgress(chainParams.TxData(), pindex);\n        double dProgressTip = GuessVerificationProgress(chainParams.TxData(), chainActive.Tip());\n        while (pindex)\n        {\n            // A temporary fix for inability to Ctrl-C rescan when restoring a wallet (will be fixed in 0.15.)\n            if (ShutdownRequested())\n                return nullptr;\n            if (pindex->nHeight % 100 == 0 && dProgressTip - dProgressStart > 0.0)\n                ShowProgress(_("Rescanning..."), std::max(1, std::min(99, (int)((GuessVerificationProgress(chainParams.TxData(), pindex) - dProgressStart) / (dProgressTip - dProgressStart) * 100))));\n            if (GetTime() >= nNow + 60) {\n                nNow = GetTime();\n                LogPrintf("Still rescanning. At block %d. Progress=%f\\n", pindex->nHeight, GuessVerificationProgress(chainParams.TxData(), pindex));\n            }\n\n            CBlock block;\n            if (ReadBlockFromDisk(block, pindex, Params().GetConsensus())) {\n                for (size_t posInBlock = 0; posInBlock < block.vtx.size(); ++posInBlock) {\n                    AddToWalletIfInvolvingMe(*block.vtx[posInBlock], pindex, posInBlock, fUpdate);\n                }\n                if (!ret) {\n                    ret = pindex;\n                }\n            } else {\n                ret = nullptr;\n            }\n            pindex = chainActive.Next(pindex);\n        }\n        ShowProgress(_("Rescanning..."), 100); // hide progress dialog in GUI\n    }\n    return ret;\n}\n\nvoid CWallet::ReacceptWalletTransactions()\n{\n    // If transactions aren\'t being broadcasted, don\'t let them into local mempool either\n    if (!fBroadcastTransactions)\n        return;\n    LOCK2(cs_main, cs_wallet);\n    std::map<int64_t, CWalletTx*> mapSorted;\n\n    // Sort pending wallet transactions based on their initial wallet insertion order\n    BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)\n    {\n        const uint256& wtxid = item.first;\n        CWalletTx& wtx = item.second;\n        assert(wtx.GetHash() == wtxid);\n\n        int nDepth = wtx.GetDepthInMainChain();\n\n        if (!wtx.IsCoinBase() && (nDepth == 0 && !wtx.isAbandoned() && !wtx.IsLockedByLLMQInstantSend())) {\n            mapSorted.insert(std::make_pair(wtx.nOrderPos, &wtx));\n        }\n    }\n\n    // Try to add wallet transactions to memory pool\n    BOOST_FOREACH(PAIRTYPE(const int64_t, CWalletTx*)& item, mapSorted)\n    {\n        CWalletTx& wtx = *(item.second);\n        CValidationState state;\n        // the app was closed and re-opened, do NOT check their\n        // serial numbers, and DO NOT try to mark their serial numbers\n        // a second time. We assume those operations were already done.\n        wtx.AcceptToMemoryPool(maxTxFee, state);\n        // If Dandelion enabled, relay transaction once again.\n        if (GetBoolArg("-dandelion", true)) {\n            wtx.RelayWalletTransaction(g_connman.get());\n        }\n    }\n}\n\nbool CWalletTx::RelayWalletTransaction(CConnman* connman)\n{\n    assert(pwallet->GetBroadcastTransactions());\n    if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain() == 0)\n    {\n        CValidationState state;\n        /* GetDepthInMainChain already catches known conflicts. */\n        if (InMempool() || InStempool() || AcceptToMemoryPool(maxTxFee, state))\n        {\n            // If Dandelion enabled, push inventory item to just one destination.\n            if (GetBoolArg("-dandelion", true)) {\n                int64_t nCurrTime = GetTimeMicros();\n                int64_t nEmbargo = 1000000 * DANDELION_EMBARGO_MINIMUM\n                        + PoissonNextSend(nCurrTime, DANDELION_EMBARGO_AVG_ADD);\n                CNode::insertDandelionEmbargo(GetHash(), nEmbargo);\n                CInv inv(MSG_DANDELION_TX, GetHash());\n                return CNode::localDandelionDestinationPushInventory(inv);\n            }\n            else {\n                // LogPrintf("Relaying wtx %s\\n", GetHash().ToString());\n                if (connman) {\n                    connman->RelayTransaction(*this);\n                    return true;\n                }\n            }\n        }\n    }\n\n    return false;\n}\n\nstd::set<uint256> CWalletTx::GetConflicts() const\n{\n    std::set<uint256> result;\n    if (pwallet != NULL)\n    {\n        uint256 myHash = GetHash();\n        result = pwallet->GetConflicts(myHash);\n        result.erase(myHash);\n    }\n    return result;\n}\n\nCAmount CWalletTx::GetDebit(const isminefilter& filter) const\n{\n    if (tx->vin.empty())\n        return 0;\n\n    CAmount debit = 0;\n    if(filter & ISMINE_SPENDABLE)\n    {\n        if (fDebitCached)\n            debit += nDebitCached;\n        else\n        {\n            nDebitCached = pwallet->GetDebit(*this, ISMINE_SPENDABLE);\n            fDebitCached = true;\n            debit += nDebitCached;\n        }\n    }\n    if(filter & ISMINE_WATCH_ONLY)\n    {\n        if(fWatchDebitCached)\n            debit += nWatchDebitCached;\n        else\n        {\n            nWatchDebitCached = pwallet->GetDebit(*this, ISMINE_WATCH_ONLY);\n            fWatchDebitCached = true;\n            debit += nWatchDebitCached;\n        }\n    }\n    return debit;\n}\n\nCAmount CWalletTx::GetCredit(const isminefilter& filter) const\n{\n    // Must wait until coinbase is safely deep enough in the chain before valuing it\n    if (IsCoinBase() && GetBlocksToMaturity() > 0)\n        return 0;\n\n    CAmount credit = 0;\n    if (filter & ISMINE_SPENDABLE)\n    {\n        // GetBalance can assume transactions in mapWallet won\'t change\n        if (fCreditCached)\n            credit += nCreditCached;\n        else\n        {\n            nCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);\n            fCreditCached = true;\n            credit += nCreditCached;\n        }\n    }\n    if (filter & ISMINE_WATCH_ONLY)\n    {\n        if (fWatchCreditCached)\n            credit += nWatchCreditCached;\n        else\n        {\n            nWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);\n            fWatchCreditCached = true;\n            credit += nWatchCreditCached;\n        }\n    }\n    return credit;\n}\n\nCAmount CWalletTx::GetImmatureCredit(bool fUseCache) const\n{\n    if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())\n    {\n        if (fUseCache && fImmatureCreditCached)\n            return nImmatureCreditCached;\n        nImmatureCreditCached = pwallet->GetCredit(*this, ISMINE_SPENDABLE);\n        fImmatureCreditCached = true;\n        return nImmatureCreditCached;\n    }\n\n    return 0;\n}\n\nCAmount CWalletTx::GetAvailableCredit(bool fUseCache, bool fExcludeLocked) const {\n    if (pwallet == 0)\n        return 0;\n\n    // Must wait until coinbase is safely deep enough in the chain before valuing it\n    if (IsCoinBase() && GetBlocksToMaturity() > 0)\n        return 0;\n\n    // We cannot use cache if vout contains mints due to it will not update when it spend\n    if (fUseCache && fAvailableCreditCached && !tx->IsZerocoinMint() && !tx->IsSigmaMint() && !tx->IsLelantusMint() && !fExcludeLocked)\n        return nAvailableCreditCached;\n\n    CAmount nCredit = 0;\n    uint256 hashTx = GetHash();\n    for (unsigned int i = 0; i < tx->vout.size(); i++)\n    {\n        if (!pwallet->IsSpent(hashTx, i))\n        {\n            const CTxOut &txout = tx->vout[i];\n            bool isPrivate = txout.scriptPubKey.IsZerocoinMint() || txout.scriptPubKey.IsSigmaMint() || txout.scriptPubKey.IsLelantusMint() || txout.scriptPubKey.IsLelantusJMint();\n            bool condition = isPrivate;\n            if (fExcludeLocked)\n                condition = (isPrivate || pwallet->IsLockedCoin(hashTx, i));\n            nCredit += condition ? 0 : pwallet->GetCredit(txout, ISMINE_SPENDABLE);\n            if (!MoneyRange(nCredit))\n                throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");\n        }\n    }\n\n    nAvailableCreditCached = nCredit;\n    fAvailableCreditCached = true;\n\n    if (fExcludeLocked)\n        fAvailableCreditCached = false;\n\n    return nCredit;\n}\n\nCAmount CWalletTx::GetImmatureWatchOnlyCredit(const bool& fUseCache) const\n{\n    if (IsCoinBase() && GetBlocksToMaturity() > 0 && IsInMainChain())\n    {\n        if (fUseCache && fImmatureWatchCreditCached)\n            return nImmatureWatchCreditCached;\n        nImmatureWatchCreditCached = pwallet->GetCredit(*this, ISMINE_WATCH_ONLY);\n        fImmatureWatchCreditCached = true;\n        return nImmatureWatchCreditCached;\n    }\n\n    return 0;\n}\n\nCAmount CWalletTx::GetAvailableWatchOnlyCredit(const bool& fUseCache) const\n{\n    if (pwallet == 0)\n        return 0;\n\n    // Must wait until coinbase is safely deep enough in the chain before valuing it\n    if (IsCoinBase() && GetBlocksToMaturity() > 0)\n        return 0;\n\n    if (fUseCache && fAvailableWatchCreditCached)\n        return nAvailableWatchCreditCached;\n\n    CAmount nCredit = 0;\n    for (unsigned int i = 0; i < tx->vout.size(); i++)\n    {\n        if (!pwallet->IsSpent(GetHash(), i))\n        {\n            const CTxOut &txout = tx->vout[i];\n            nCredit += pwallet->GetCredit(txout, ISMINE_WATCH_ONLY);\n            if (!MoneyRange(nCredit))\n                throw std::runtime_error("CWalletTx::GetAvailableCredit() : value out of range");\n        }\n    }\n\n    nAvailableWatchCreditCached = nCredit;\n    fAvailableWatchCreditCached = true;\n    return nCredit;\n}\n\nCAmount CWalletTx::GetChange() const\n{\n    if (fChangeCached)\n        return nChangeCached;\n    nChangeCached = pwallet->GetChange(*this);\n    fChangeCached = true;\n    return nChangeCached;\n}\n\nbool CWalletTx::InMempool() const\n{\n    LOCK(mempool.cs);\n    if (mempool.exists(GetHash())) {\n        return true;\n    }\n    return false;\n}\n\nbool CWalletTx::InStempool() const\n{\n    if (txpools.getStemTxPool().exists(GetHash())) {\n        return true;\n    }\n    return false;\n}\n\nbool CWalletTx::IsTrusted() const\n{\n    // Quick answer in most cases.\n    // Zerocoin spend is always false due to it use nSequence incorrectly.\n    if (!tx->IsZerocoinSpend() && !CheckFinalTx(*this))\n        return false;\n    int nDepth = GetDepthInMainChain();\n    if (nDepth >= 1)\n        return true;\n    if (nDepth < 0)\n        return false;\n    if (IsLockedByLLMQInstantSend())\n        return true;\n    if (!bSpendZeroConfChange || !IsFromMe(ISMINE_ALL)) // using wtx\'s cached debit\n        return false;\n\n    // Don\'t trust unconfirmed transactions from us unless they are in the mempool.\n    if (!InMempool() && !InStempool())\n        return false;\n\n    // Trusted if all inputs are from us and are in the mempool:\n    BOOST_FOREACH(const CTxIn& txin, tx->vin)\n    {\n        if (txin.IsZerocoinSpend() || txin.IsSigmaSpend() || txin.IsZerocoinRemint() || txin.IsLelantusJoinSplit()) {\n            if (!(pwallet->IsMine(txin, *tx) & ISMINE_SPENDABLE)) {\n                return false;\n            }\n        } else {\n            // Transactions not sent by us: not trusted\n            const CWalletTx *parent = pwallet->GetWalletTx(txin.prevout.hash);\n            if (parent == NULL)\n                return false;\n            const CTxOut &parentOut = parent->tx->vout[txin.prevout.n];\n            if (pwallet->IsMine(parentOut) != ISMINE_SPENDABLE)\n                return false;\n        }\n    }\n\n    return true;\n}\n\nbool CWalletTx::IsChange(uint32_t out) const {\n    if (out >= tx->vout.size()) {\n        throw std::invalid_argument("The specified output index is not valid");\n    }\n\n    if (changes.count(out)) {\n        return true;\n    }\n\n    // Legacy transaction handling.\n    // Zerocoin spend have one special output mode to spend to yourself with change address,\n    // we don\'t want to identify that output as change.\n    if (!tx->IsZerocoinSpend() && ::IsMine(*pwallet, tx->vout[out].scriptPubKey)) {\n        CTxDestination address;\n        if (!ExtractDestination(tx->vout[out].scriptPubKey, address)) {\n            return true;\n        }\n\n        LOCK(pwallet->cs_wallet);\n        if (!pwallet->mapAddressBook.count(address)) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nbool CWalletTx::IsChange(const CTxOut& out) const {\n    auto it = std::find(tx->vout.begin(), tx->vout.end(), out);\n    if (it == tx->vout.end()) {\n        throw std::invalid_argument("The specified output does not belong to the transaction");\n    }\n\n    return IsChange(it - tx->vout.begin());\n}\n\nbool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const\n{\n        CMutableTransaction tx1 = *this->tx;\n        CMutableTransaction tx2 = *_tx.tx;\n        for (unsigned int i = 0; i < tx1.vin.size(); i++) tx1.vin[i].scriptSig = CScript();\n        for (unsigned int i = 0; i < tx2.vin.size(); i++) tx2.vin[i].scriptSig = CScript();\n        return CTransaction(tx1) == CTransaction(tx2);\n}\n\nstd::vector<uint256> CWallet::ResendWalletTransactionsBefore(int64_t nTime, CConnman* connman)\n{\n    std::vector<uint256> result;\n\n    LOCK(cs_wallet);\n    // Sort them in chronological order\n    std::multimap<unsigned int, CWalletTx*> mapSorted;\n    BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)\n    {\n        CWalletTx& wtx = item.second;\n        // Don\'t rebroadcast if newer than nTime:\n        if (wtx.nTimeReceived > nTime)\n            continue;\n        mapSorted.insert(std::make_pair(wtx.nTimeReceived, &wtx));\n    }\n    BOOST_FOREACH(PAIRTYPE(const unsigned int, CWalletTx*)& item, mapSorted)\n    {\n        CWalletTx& wtx = *item.second;\n        if (wtx.RelayWalletTransaction(connman))\n            result.push_back(wtx.GetHash());\n    }\n    return result;\n}\n\nvoid CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman)\n{\n    // Do this infrequently and randomly to avoid giving away\n    // that these are our transactions.\n    if (GetTime() < nNextResend || !fBroadcastTransactions)\n        return;\n    bool fFirst = (nNextResend == 0);\n    nNextResend = GetTime() + GetRand(30 * 60);\n    if (fFirst)\n        return;\n\n    // Only do it if there\'s been a new block since last time\n    if (nBestBlockTime < nLastResend)\n        return;\n    nLastResend = GetTime();\n\n    // Rebroadcast unconfirmed txes older than 5 minutes before the last\n    // block was found:\n    std::vector<uint256> relayed = ResendWalletTransactionsBefore(nBestBlockTime-5*60, connman);\n    if (!relayed.empty())\n        LogPrintf("%s: rebroadcast %u unconfirmed transactions\\n", __func__, relayed.size());\n}\n\n/** @} */ // end of mapWallet\n\n\n\n\n/** @defgroup Actions\n *\n * @{\n */\n\n\nCAmount CWallet::GetBalance(bool fExcludeLocked) const\n{\n    CAmount nTotal = 0;\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\n        {\n            const CWalletTx* pcoin = &(*it).second;\n            if (pcoin->IsTrusted())\n                nTotal += pcoin->GetAvailableCredit(true, fExcludeLocked);\n        }\n    }\n\n    return nTotal;\n}\n\nstd::pair<CAmount, CAmount> CWallet::GetPrivateBalance() const\n{\n    size_t confirmed, unconfirmed;\n    return GetPrivateBalance(confirmed, unconfirmed);\n}\n\nstd::pair<CAmount, CAmount> CWallet::GetPrivateBalance(size_t &confirmed, size_t &unconfirmed) const\n{\n    std::pair<CAmount, CAmount> balance = {0, 0};\n\n    confirmed = 0;\n    unconfirmed = 0;\n\n    auto zwallet = pwalletMain->zwallet.get();\n\n    if(!zwallet)\n        return balance;\n\n    auto lelantusCoins = zwallet->GetTracker().ListLelantusMints(true, false, false);\n    for (auto const &c : lelantusCoins) {\n\n        if (c.isUsed || c.isArchived || !c.isSeedCorrect) {\n            continue;\n        }\n\n        auto conf = c.nHeight > 0\n            ? chainActive.Height() - c.nHeight + 1 : 0;\n\n        if (conf >= ZC_MINT_CONFIRMATIONS) {\n            confirmed++;\n            balance.first += c.amount;\n        } else {\n            unconfirmed++;\n            balance.second += c.amount;\n        }\n    }\n\n    auto sigmaCoins = zwallet->GetTracker().ListMints(true, false, false);\n    for (auto const &c : sigmaCoins) {\n\n        if (c.isUsed || c.isArchived || !c.isSeedCorrect) {\n            continue;\n        }\n\n        CAmount amount;\n        if (!sigma::DenominationToInteger(c.denom, amount)) {\n            throw std::runtime_error("Fail to get denomination value");\n        }\n\n        auto conf = c.nHeight > 0\n            ? chainActive.Height() - c.nHeight + 1 : 0;\n\n        if (conf >= ZC_MINT_CONFIRMATIONS) {\n            confirmed++;\n            balance.first += amount;\n        } else {\n            unconfirmed++;\n            balance.second += amount;\n        }\n    }\n\n    return balance;\n}\n\nstd::vector<CRecipient> CWallet::CreateSigmaMintRecipients(\n    std::vector<sigma::PrivateCoin>& coins,\n    std::vector<CHDMint>& vDMints)\n{\n    EnsureMintWalletAvailable();\n\n    std::vector<CRecipient> vecSend;\n    CWalletDB walletdb(pwalletMain->strWalletFile);\n\n    std::transform(coins.begin(), coins.end(), std::back_inserter(vecSend),\n        [&vDMints, &walletdb](sigma::PrivateCoin& coin) -> CRecipient {\n\n            // Generate and store secrets deterministically in the following function.\n            CHDMint dMint;\n            pwalletMain->zwallet->GenerateMint(walletdb, coin.getPublicCoin().getDenomination(), coin, dMint);\n\n\n            // Get a copy of the \'public\' portion of the coin. You should\n            // embed this into a Signa \'MINT\' transaction along with a series\n            // of currency inputs totaling the assigned value of one sigma.\n            auto& pubCoin = coin.getPublicCoin();\n\n            if (!pubCoin.validate()) {\n                throw std::runtime_error("Unable to mint a sigma coin.");\n            }\n\n            // Create script for coin\n            CScript scriptSerializedCoin;\n            // opcode is inserted as 1 byte according to file script/script.h\n            scriptSerializedCoin << OP_SIGMAMINT;\n\n            // and this one will write the size in different byte lengths depending on the length of vector. If vector size is <0.4c, which is 76, will write the size of vector in just 1 byte. In our case the size is always 34, so must write that 34 in 1 byte.\n            std::vector<unsigned char> vch = pubCoin.getValue().getvch();\n            scriptSerializedCoin.insert(scriptSerializedCoin.end(), vch.begin(), vch.end());\n\n            CAmount v;\n            DenominationToInteger(pubCoin.getDenomination(), v);\n\n            vDMints.push_back(dMint);\n\n            return {scriptSerializedCoin, v, false};\n        }\n    );\n\n    return vecSend;\n}\n\nCRecipient CWallet::CreateLelantusMintRecipient(\n        lelantus::PrivateCoin& coin,\n        CHDMint& vDMint,\n        bool generate)\n{\n    EnsureMintWalletAvailable();\n\n    while (true) {\n        CWalletDB walletdb(pwalletMain->strWalletFile);\n        uint160 seedID;\n        if (generate) {\n            // Generate and store secrets deterministically in the following function.\n            pwalletMain->zwallet->GenerateLelantusMint(walletdb, coin, vDMint, seedID);\n        }\n\n        // Get a copy of the \'public\' portion of the coin. You should\n        // embed this into a Lelantus \'MINT\' transaction along with a series of currency inputs\n        auto &pubCoin = coin.getPublicCoin();\n\n        if (!pubCoin.validate()) {\n            throw std::runtime_error("Unable to mint a lelantus coin.");\n        }\n\n        // Create script for coin\n        CScript script;\n        // opcode is inserted as 1 byte according to file script/script.h\n        script << OP_LELANTUSMINT;\n\n        // and this one will write the size in different byte lengths depending on the length of vector. If vector size is <0.4c, which is 76, will write the size of vector in just 1 byte. In our case the size is always 34, so must write that 34 in 1 byte.\n        std::vector<unsigned char> vch = pubCoin.getValue().getvch();\n        script.insert(script.end(), vch.begin(), vch.end()); //this uses 34 byte\n\n        // generating schnorr proof\n        CDataStream serializedSchnorrProof(SER_NETWORK, PROTOCOL_VERSION);\n        lelantus::GenerateMintSchnorrProof(coin, serializedSchnorrProof);\n        script.insert(script.end(), serializedSchnorrProof.begin(), serializedSchnorrProof.end()); //this uses 98 byte\n\n        auto pubcoin = vDMint.GetPubcoinValue() +\n                       lelantus::Params::get_default()->get_h1() * Scalar(vDMint.GetAmount()).negate();\n        uint256 hashPub = primitives::GetPubCoinValueHash(pubcoin);\n        CDataStream ss(SER_GETHASH, 0);\n        ss << hashPub;\n        ss << seedID;\n        uint256 hashForRecover = Hash(ss.begin(), ss.end());\n\n        // Check if there is a mint with same private data in chain, most likely Hd mint state corruption,\n        // If yes, try with new counter\n        GroupElement dummyValue;\n        if (lelantus::CLelantusState::GetState()->HasCoinTag(dummyValue, hashForRecover) ||\n            sigma::CSigmaState::GetState()->HasCoinHash(dummyValue, hashPub))\n            continue;\n\n        CDataStream serializedHash(SER_NETWORK, 0);\n        serializedHash << hashForRecover;\n        script.insert(script.end(), serializedHash.begin(), serializedHash.end());\n\n        // overall Lelantus mint script size is 1 + 34 + 98 + 32 = 165 byte\n        return {script, CAmount(coin.getV()), false};\n    }\n}\n\n// coinsIn has to be sorted in descending order.\nint CWallet::GetRequiredCoinCountForAmount(\n        const CAmount& required,\n        const std::vector<sigma::CoinDenomination>& denominations) {\n    CAmount val = required;\n    int result = 0;\n    for (std::size_t i = 0; i < denominations.size(); i++)\n    {\n        CAmount denom;\n        DenominationToInteger(denominations[i], denom);\n        while (val >= denom) {\n            val -= denom;\n            result++;\n        }\n    }\n\n    return result;\n}\n\n/** \\brief denominations has to be sorted in descending order. Each denomination can be used multiple times.\n *\n *  \\returns The amount which was possible to actually mint.\n */\nCAmount CWallet::SelectMintCoinsForAmount(\n        const CAmount& required,\n        const std::vector<sigma::CoinDenomination>& denominations,\n        std::vector<sigma::CoinDenomination>& coinsOut) {\n    CAmount val = required;\n    for (std::size_t i = 0; i < denominations.size(); i++)\n    {\n        CAmount denom;\n        DenominationToInteger(denominations[i], denom);\n        while (val >= denom)\n        {\n            val -= denom;\n            coinsOut.push_back(denominations[i]);\n        }\n    }\n\n    return required - val;\n}\n\n/** \\brief coinsIn has to be sorted in descending order. Each coin can be used only once.\n *\n *  \\returns The amount which was possible to actually spend.\n */\nCAmount CWallet::SelectSpendCoinsForAmount(\n        const CAmount& required,\n        const std::list<CSigmaEntry>& coinsIn,\n        std::vector<CSigmaEntry>& coinsOut) {\n    CAmount val = required;\n    for (auto coinIt = coinsIn.begin(); coinIt != coinsIn.end(); coinIt++)\n    {\n        if (coinIt->IsUsed)\n          continue;\n        CAmount denom = coinIt->get_denomination_value();\n        if (val >= denom)\n        {\n            val -= denom;\n            coinsOut.push_back(*coinIt);\n        }\n    }\n\n    return required - val;\n}\n\n// Calculate total balance in a different way from GetBalance. The biggest\n// difference is that GetBalance sums up all unspent TxOuts paying to the\n// wallet, while this sums up both spent and unspent TxOuts paying to the\n// wallet, and then subtracts the values of TxIns spending from the wallet. This\n// also has fewer restrictions on which unconfirmed transactions are considered\n// trusted.\nCAmount CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth, const std::string* account, bool fAddLocked) const\n{\n    LOCK2(cs_main, cs_wallet);\n\n    CAmount balance = 0;\n    for (const auto& entry : mapWallet) {\n        const CWalletTx& wtx = entry.second;\n        const int depth = wtx.GetDepthInMainChain();\n        if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.GetBlocksToMaturity() > 0) {\n            continue;\n        }\n\n        // Loop through tx outputs and add incoming payments. For outgoing txs,\n        // treat change outputs specially, as part of the amount debited.\n        CAmount debit = wtx.GetDebit(filter);\n        const bool outgoing = debit > 0;\n        for (const CTxOut& out : wtx.tx->vout) {\n            if (outgoing && IsChange(wtx.tx->GetHash(), out)) {\n                debit -= out.nValue;\n            } else if (IsMine(out) & filter && depth >= minDepth && (!account || *account == GetAccountName(out.scriptPubKey))) {\n                balance += out.nValue;\n            }\n        }\n\n        // For outgoing txs, subtract amount debited.\n        if (outgoing && (!account || *account == wtx.strFromAccount)) {\n            balance -= debit;\n        }\n    }\n\n    if (account) {\n        balance += CWalletDB(strWalletFile).GetAccountCreditDebit(*account);\n    }\n\n    return balance;\n}\n\nstd::list<CSigmaEntry> CWallet::GetAvailableCoins(const CCoinControl *coinControl, bool includeUnsafe, bool forEstimation) const {\n    EnsureMintWalletAvailable();\n    LOCK2(cs_main, cs_wallet);\n    CWalletDB walletdb(strWalletFile);\n    std::list<CSigmaEntry> coins;\n    std::vector<CMintMeta> vecMints = zwallet->GetTracker().ListMints(true, true, false);\n    std::list<CMintMeta> listMints(vecMints.begin(), vecMints.end());\n    for (const CMintMeta& mint : listMints) {\n        CSigmaEntry entry;\n        GetMint(mint.hashSerial, entry, forEstimation);\n        coins.push_back(entry);\n    }\n\n    std::set<COutPoint> lockedCoins = setLockedCoins;\n\n    // Filter out coins which are not confirmed, I.E. do not have at least 2 blocks\n    // above them, after they were minted.\n    // Also filter out used coins.\n    // Finally filter out coins that have not been selected from CoinControl should that be used\n    coins.remove_if([lockedCoins, coinControl, includeUnsafe](const CSigmaEntry& coin) {\n        sigma::CSigmaState* sigmaState = sigma::CSigmaState::GetState();\n        if (coin.IsUsed)\n            return true;\n\n        int coinHeight, coinId;\n        std::tie(coinHeight, coinId) =  sigmaState->GetMintedCoinHeightAndId(\n            sigma::PublicCoin(coin.value, coin.get_denomination()));\n\n        // Check group size\n        uint256 hashOut;\n        std::vector<sigma::PublicCoin> coinOuts;\n        sigmaState->GetCoinSetForSpend(\n            &chainActive,\n            chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), // required 1 confirmation for mint to spend\n            coin.get_denomination(),\n            coinId,\n            hashOut,\n            coinOuts\n        );\n\n        if (!includeUnsafe && coinOuts.size() < 2) {\n            return true;\n        }\n\n        if (coinHeight == -1) {\n            // Coin still in the mempool.\n            return true;\n        }\n\n        if (coinHeight + (ZC_MINT_CONFIRMATIONS - 1) > chainActive.Height()) {\n            // Remove the coin from the candidates list, since it does not have the\n            // required number of confirmations.\n            return true;\n        }\n\n        COutPoint outPoint;\n        sigma::PublicCoin pubCoin(coin.value, coin.get_denomination());\n        sigma::GetOutPoint(outPoint, pubCoin);\n\n        if(lockedCoins.count(outPoint) > 0){\n            return true;\n        }\n\n        if(coinControl != NULL){\n            if(coinControl->HasSelected()){\n                if(!coinControl->IsSelected(outPoint)){\n                    return true;\n                }\n            }\n        }\n\n        return false;\n    });\n\n    return coins;\n}\n\nstd::list<CLelantusEntry> CWallet::GetAvailableLelantusCoins(const CCoinControl *coinControl, bool includeUnsafe, bool forEstimation) const {\n    EnsureMintWalletAvailable();\n\n    LOCK2(cs_main, cs_wallet);\n    CWalletDB walletdb(strWalletFile);\n    std::list<CLelantusEntry> coins;\n    std::vector<CLelantusMintMeta> vecMints = zwallet->GetTracker().ListLelantusMints(true, true, false);\n    for (const CLelantusMintMeta& mint : vecMints) {\n        CLelantusEntry entry;\n        GetMint(mint.hashSerial, entry, forEstimation);\n        if(entry.amount != 0) // ignore 0 mints which where created to increase privacy\n            coins.push_back(entry);\n    }\n\n    std::set<COutPoint> lockedCoins = setLockedCoins;\n\n    // Filter out coins which are not confirmed, I.E. do not have at least 2 blocks\n    // above them, after they were minted.\n    // Also filter out used coins.\n    // Finally filter out coins that have not been selected from CoinControl should that be used\n    coins.remove_if([lockedCoins, coinControl, includeUnsafe](const CLelantusEntry& coin) {\n        lelantus::CLelantusState* state = lelantus::CLelantusState::GetState();\n        if (coin.IsUsed)\n            return true;\n\n        COutPoint outPoint;\n        lelantus::PublicCoin pubCoin(coin.value);\n        lelantus::GetOutPoint(outPoint, pubCoin);\n\n        if(lockedCoins.count(outPoint) > 0){\n            return true;\n        }\n\n        if(coinControl != NULL){\n            if(coinControl->HasSelected()){\n                if(!coinControl->IsSelected(outPoint)){\n                    return true;\n                }\n            }\n        }\n\n        int coinHeight, coinId;\n        std::tie(coinHeight, coinId) =  state->GetMintedCoinHeightAndId(lelantus::PublicCoin(coin.value));\n\n        // Check group size\n        uint256 hashOut;\n        std::vector<lelantus::PublicCoin> coinOuts;\n        std::vector<unsigned char> setHash;\n        state->GetCoinSetForSpend(\n            &chainActive,\n            chainActive.Height() - (ZC_MINT_CONFIRMATIONS - 1), // required 1 confirmation for mint to spend\n            coinId,\n            hashOut,\n            coinOuts,\n            setHash\n        );\n\n        if (!includeUnsafe && coinOuts.size() < 2) {\n            return true;\n        }\n\n        if (coinHeight == -1) {\n            // Coin still in the mempool.\n            return true;\n        }\n\n        if (coinHeight + (ZC_MINT_CONFIRMATIONS - 1) > chainActive.Height()) {\n            // Remove the coin from the candidates list, since it does not have the\n            // required number of confirmations.\n            return true;\n        }\n\n        return false;\n    });\n\n    return coins;\n}\n\nstd::vector<unsigned char> GetAESKey(const secp_primitives::GroupElement& pubcoin) {\n    uint32_t keyPath = primitives::GetPubCoinValueHash(pubcoin).GetFirstUint32();\n    CKey secret;\n    {\n        pwalletMain->GetKeyFromKeypath(BIP44_MINT_VALUE_INDEX, keyPath, secret);\n    }\n\n    std::vector<unsigned char> result(CHMAC_SHA512::OUTPUT_SIZE);\n\n    CHMAC_SHA512(secret.begin(), secret.size()).Finalize(&result[0]);\n    return result;\n}\n\nstd::vector<unsigned char> CWallet::EncryptMintAmount(uint64_t amount, const secp_primitives::GroupElement& pubcoin) const {\n    LOCK(cs_wallet);\n    std::vector<unsigned char> key = GetAESKey(pubcoin);\n    AES256Encrypt enc(key.data());\n    std::vector<unsigned char> ciphertext(16);\n    std::vector<unsigned char> plaintext(16);\n    memcpy(plaintext.data(), &amount, 8);\n    enc.Encrypt(ciphertext.data(), plaintext.data());\n    return ciphertext;\n}\n\nbool CWallet::DecryptMintAmount(const std::vector<unsigned char>& encryptedValue, const secp_primitives::GroupElement& pubcoin, uint64_t& amount) const {\n    if (IsLocked() || hdChain.masterKeyID.IsNull()) {\n        amount = 0;\n        return true;\n    }\n\n    LOCK(cs_wallet);\n    std::vector<unsigned char> key = GetAESKey(pubcoin);\n    AES256Decrypt dec(key.data());\n    std::vector<unsigned char> plaintext(16);\n    dec.Decrypt(plaintext.data(), encryptedValue.data());\n    memcpy(&amount, plaintext.data(), 8);\n    return true;\n}\n\n\ntemplate<typename Iterator>\nstatic CAmount CalculateCoinsBalance(Iterator begin, Iterator end) {\n    CAmount balance(0);\n    for (auto start = begin; start != end; start++) {\n        balance += start->get_denomination_value();\n    }\n    return balance;\n}\n\ntemplate<typename Iterator>\nstatic CAmount CalculateLelantusCoinsBalance(Iterator begin, Iterator end) {\n    CAmount balance(0);\n    for (auto start = begin; start != end; start++) {\n        balance += start->amount;\n    }\n    return balance;\n}\n\nbool CWallet::GetCoinsToSpend(\n        CAmount required,\n        std::vector<CSigmaEntry>& coinsToSpend_out,\n        std::vector<sigma::CoinDenomination>& coinsToMint_out,\n        std::list<CSigmaEntry>& coins,\n        const size_t coinsToSpendLimit,\n        const CAmount amountToSpendLimit,\n        const CCoinControl *coinControl) const\n{\n    // Sanity check to make sure this function is never called with a too large\n    // amount to spend, resulting to a possible crash due to out of memory condition.\n    if (!MoneyRange(required)) {\n        throw std::invalid_argument("Request to spend more than 21 MLN firos.\\n");\n    }\n\n    if (!MoneyRange(amountToSpendLimit)) {\n        throw std::invalid_argument(_("Amount limit is exceed max money"));\n    }\n\n    // We have Coins denomination * 10^8, we divide with 0.05 * 10^8 and add one coin of\n    // denomination 100 (also divide by 0.05 * 10^8)\n    constexpr CAmount zeros(5000000);\n\n    // Rounding, Anything below 0.05 coin goes to the miners as a fee.\n    int roundedRequired = required / zeros;\n    if (required % zeros != 0) {\n        ++roundedRequired;\n    }\n\n    int limitVal = amountToSpendLimit / zeros;\n\n    if (roundedRequired > limitVal) {\n        throw std::invalid_argument(\n            _("Required amount exceed value spend limit"));\n    }\n\n    CAmount availableBalance = CalculateCoinsBalance(coins.begin(), coins.end());\n\n    if (roundedRequired * zeros > availableBalance) {\n        throw InsufficientFunds();\n    }\n\n    // sort by highest denomination. if it is same denomination we will prefer the previous block\n    auto comparer = [](const CSigmaEntry& a, const CSigmaEntry& b) -> bool {\n        return a.get_denomination_value() != b.get_denomination_value() ? a.get_denomination_value() > b.get_denomination_value() : a.nHeight < b.nHeight;\n    };\n    coins.sort(comparer);\n\n    std::vector<sigma::CoinDenomination> denominations;\n    sigma::GetAllDenoms(denominations);\n\n    // Value of the largest coin, I.E. 100 for now.\n    CAmount max_coin_value;\n    if (!DenominationToInteger(denominations[0], max_coin_value)) {\n        throw std::runtime_error("Unknown sigma denomination.\\n");\n    }\n\n    int val = roundedRequired + max_coin_value / zeros;\n\n    // val represent max value in range that we will search which may be over limit.\n    // then we trim it out because we never use it.\n    val = std::min(val, limitVal);\n\n    // We need only last 2 rows of matrix of knapsack algorithm.\n    std::vector<uint64_t> prev_row;\n    prev_row.resize(val + 1);\n\n    std::vector<uint64_t> next_row(val + 1, (INT_MAX - 1) / 2);\n\n    auto coinIt = coins.rbegin();\n    next_row[0] = 0;\n    next_row[coinIt->get_denomination_value() / zeros] = 1;\n    ++coinIt;\n\n    for (; coinIt != coins.rend(); coinIt++) {\n        std::swap(prev_row, next_row);\n        CAmount denom_i = coinIt->get_denomination_value() / zeros;\n        for (int j = 1; j <= val; j++) {\n            next_row[j] = prev_row[j];\n            if (j >= denom_i &&  next_row[j] > prev_row[j - denom_i] + 1) {\n                    next_row[j] = prev_row[j - denom_i] + 1;\n            }\n        }\n    }\n\n    int index = val;\n    uint64_t best_spend_val = 0;\n\n    // If coinControl, want to use all inputs\n    bool coinControlUsed = false;\n    if(coinControl != NULL){\n        if(coinControl->HasSelected()){\n            auto coinIt = coins.rbegin();\n            for (; coinIt != coins.rend(); coinIt++) {\n                best_spend_val += coinIt->get_denomination_value();\n            }\n            coinControlUsed = true;\n        }\n    }\n    if(!coinControlUsed) {\n        best_spend_val = val;\n        int minimum = INT_MAX - 1;\n        while(index >= roundedRequired) {\n            int temp_min = next_row[index] + GetRequiredCoinCountForAmount(\n                (index - roundedRequired) * zeros, denominations);\n            if (minimum > temp_min && next_row[index] != (INT_MAX - 1) / 2 && next_row[index] <= coinsToSpendLimit) {\n                best_spend_val = index;\n                minimum = temp_min;\n            }\n            --index;\n        }\n        best_spend_val *= zeros;\n\n        if (minimum == INT_MAX - 1)\n            throw std::invalid_argument(\n                _("Can not choose coins within limit."));\n    }\n\n    if (SelectMintCoinsForAmount(best_spend_val - roundedRequired * zeros, denominations, coinsToMint_out) != best_spend_val - roundedRequired * zeros) {\n        throw std::invalid_argument(\n            _("Problem with coin selection for re-mint while spending."));\n    }\n    if (SelectSpendCoinsForAmount(best_spend_val, coins, coinsToSpend_out) != best_spend_val) {\n        throw std::invalid_argument(\n            _("Problem with coin selection for spend."));\n    }\n\n    return true;\n}\n\nbool CWallet::GetCoinsToJoinSplit(\n        CAmount required,\n        std::vector<CLelantusEntry>& coinsToSpend_out,\n        CAmount& changeToMint,\n        std::list<CLelantusEntry> coins,\n        const size_t coinsToSpendLimit,\n        const CAmount amountToSpendLimit,\n        const CCoinControl *coinControl) const\n{\n\n    EnsureMintWalletAvailable();\n    const Consensus::Params &consensusParams = Params().GetConsensus();\n\n    if (required > consensusParams.nMaxValueLelantusSpendPerTransaction) {\n        throw std::invalid_argument(_("The required amount exceeds spend limit"));\n    }\n\n    CAmount availableBalance = CalculateLelantusCoinsBalance(coins.begin(), coins.end());\n\n    if (required > availableBalance) {\n        throw InsufficientFunds();\n    }\n\n    // sort by biggest amount. if it is same amount we will prefer the older block\n    auto comparer = [](const CLelantusEntry& a, const CLelantusEntry& b) -> bool {\n        return a.amount != b.amount ? a.amount > b.amount : a.nHeight < b.nHeight;\n    };\n    coins.sort(comparer);\n\n    CAmount spend_val(0);\n\n    std::list<CLelantusEntry> coinsToSpend;\n\n    // If coinControl, want to use all inputs\n    bool coinControlUsed = false;\n    if(coinControl != NULL) {\n        if(coinControl->HasSelected()) {\n            auto coinIt = coins.rbegin();\n            for (; coinIt != coins.rend(); coinIt++) {\n                spend_val += coinIt->amount;\n            }\n            coinControlUsed = true;\n            coinsToSpend.insert(coinsToSpend.begin(), coins.begin(), coins.end());\n        }\n    }\n\n    if(!coinControlUsed) {\n        while (spend_val < required) {\n            if(coins.empty())\n                break;\n\n            CLelantusEntry choosen;\n            CAmount need = required - spend_val;\n\n            auto itr = coins.begin();\n            if(need >= itr->amount) {\n                choosen = *itr;\n                coins.erase(itr);\n            } else {\n                for (auto coinIt = coins.rbegin(); coinIt != coins.rend(); coinIt++) {\n                    auto nextItr = coinIt;\n                    nextItr++;\n\n                    if (coinIt->amount >= need && (nextItr == coins.rend() || nextItr->amount != coinIt->amount)) {\n                        choosen = *coinIt;\n                        coins.erase(std::next(coinIt).base());\n                        break;\n                    }\n                }\n            }\n\n            spend_val += choosen.amount;\n            coinsToSpend.push_back(choosen);\n        }\n    }\n\n    // sort by group id ay ascending order. it is mandatory for creting proper joinsplit\n    auto idComparer = [](const CLelantusEntry& a, const CLelantusEntry& b) -> bool {\n        return a.id < b.id;\n    };\n    coinsToSpend.sort(idComparer);\n\n    changeToMint = spend_val - required;\n    coinsToSpend_out.insert(coinsToSpend_out.begin(), coinsToSpend.begin(), coinsToSpend.end());\n\n    return true;\n}\n\nCAmount CWallet::GetUnconfirmedBalance() const {\n    CAmount nTotal = 0;\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) {\n            const CWalletTx *pcoin = &(*it).second;\n            if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 &&\n                (pcoin->InMempool() || pcoin->InStempool()) && !pcoin->IsLockedByLLMQInstantSend())\n                nTotal += pcoin->GetAvailableCredit();\n        }\n    }\n    return nTotal;\n}\n\nCAmount CWallet::GetImmatureBalance() const {\n    CAmount nTotal = 0;\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) {\n            const CWalletTx *pcoin = &(*it).second;\n            nTotal += pcoin->GetImmatureCredit();\n        }\n    }\n    return nTotal;\n}\n\nCAmount CWallet::GetWatchOnlyBalance() const {\n    CAmount nTotal = 0;\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) {\n            const CWalletTx *pcoin = &(*it).second;\n            if (pcoin->IsTrusted())\n                nTotal += pcoin->GetAvailableWatchOnlyCredit();\n        }\n    }\n\n    return nTotal;\n}\n\nCAmount CWallet::GetUnconfirmedWatchOnlyBalance() const {\n    CAmount nTotal = 0;\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) {\n            const CWalletTx *pcoin = &(*it).second;\n            if (!pcoin->IsTrusted() && pcoin->GetDepthInMainChain() == 0 &&\n                (pcoin->InMempool() || pcoin->InStempool()) && !pcoin->IsLockedByLLMQInstantSend())\n                nTotal += pcoin->GetAvailableWatchOnlyCredit();\n        }\n    }\n    return nTotal;\n}\n\nCAmount CWallet::GetImmatureWatchOnlyBalance() const\n{\n    CAmount nTotal = 0;\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\n        {\n            const CWalletTx* pcoin = &(*it).second;\n            nTotal += pcoin->GetImmatureWatchOnlyCredit();\n        }\n    }\n    return nTotal;\n}\n\nvoid CWallet::AvailableCoins(std::vector <COutput> &vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue, bool fUseInstantSend) const\n{\n    static const int ZNODE_COIN_REQUIRED  = 1000;\n    vCoins.clear();\n    CoinType nCoinType = coinControl ? coinControl->nCoinType : CoinType::ALL_COINS;\n\n    {\n        LOCK2(cs_main, cs_wallet);\n        int nInstantSendConfirmationsRequired = Params().GetConsensus().nInstantSendConfirmationsRequired;\n\n        for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)\n        {\n            const uint256& wtxid = it->first;\n            const CWalletTx* pcoin = &(*it).second;\n\n            if (!CheckFinalTx(*pcoin))\n                continue;\n\n            if (fOnlyConfirmed && !pcoin->IsTrusted())\n                continue;\n\n            if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)\n                continue;\n\n            int nDepth = pcoin->GetDepthInMainChain(false);\n            // do not use IX for inputs that have less then nInstantSendConfirmationsRequired blockchain confirmations\n            if (fUseInstantSend && nDepth < nInstantSendConfirmationsRequired)\n                continue;\n\n            // We should not consider coins from transactions that are replacing\n            // other transactions.\n            //\n            // Example: There is a transaction A which is replaced by bumpfee\n            // transaction B. In this case, we want to prevent creation of\n            // a transaction B\' which spends an output of B.\n            //\n            // Reason: If transaction A were initially confirmed, transactions B\n            // and B\' would no longer be valid, so the user would have to create\n            // a new transaction C to replace B\'. However, in the case of a\n            // one-block reorg, transactions B\' and C might BOTH be accepted,\n            // when the user only wanted one of them. Specifically, there could\n            // be a 1-block reorg away from the chain where transactions A and C\n            // were accepted to another chain where B, B\', and C were all\n            // accepted.\n            if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaces_txid")) {\n                continue;\n            }\n\n            // Similarly, we should not consider coins from transactions that\n            // have been replaced. In the example above, we would want to prevent\n            // creation of a transaction A\' spending an output of A, because if\n            // transaction B were initially confirmed, conflicting with A and\n            // A\', we wouldn\'t want to the user to create a transaction D\n            // intending to replace A\', but potentially resulting in a scenario\n            // where A, A\', and D could all be accepted (instead of just B and\n            // D, or just A and A\' like the user would want).\n            if (nDepth == 0 && fOnlyConfirmed && pcoin->mapValue.count("replaced_by_txid")) {\n                continue;\n            }\n\n            for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {\n                bool found = false;\n                if(nCoinType == CoinType::ALL_COINS){\n                    // We are now taking ALL_COINS to mean everything sans mints\n                    found = !(pcoin->tx->vout[i].scriptPubKey.IsZerocoinMint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsSigmaMint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsLelantusMint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint())\n                            || pcoin->tx->vout[i].scriptPubKey.IsZerocoinRemint();\n                } else if(nCoinType == CoinType::ONLY_MINTS){\n                    // Do not consider anything other than mints\n                    found = (pcoin->tx->vout[i].scriptPubKey.IsZerocoinMint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsSigmaMint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsZerocoinRemint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsLelantusMint()\n                            || pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint());\n                } else if (nCoinType == CoinType::ONLY_NOT1000IFMN) {\n                    found = !(fMasternodeMode && pcoin->tx->vout[i].nValue == ZNODE_COIN_REQUIRED * COIN);\n                } else if (nCoinType == CoinType::ONLY_NONDENOMINATED_NOT1000IFMN) {\n                    if (fMasternodeMode) found = pcoin->tx->vout[i].nValue != ZNODE_COIN_REQUIRED * COIN; // do not use Hot MN funds\n\t\t} else if (nCoinType == CoinType::ONLY_1000) {\n                    found = pcoin->tx->vout[i].nValue == ZNODE_COIN_REQUIRED * COIN;\n                } else {\n                    found = true;\n                }\n                if (!found) continue;\n\n                isminetype mine = IsMine(pcoin->tx->vout[i]);\n\n\n                if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO &&\n                    (!IsLockedCoin((*it).first, i) || nCoinType == CoinType::ONLY_1000) &&\n                    (pcoin->tx->vout[i].nValue > 0 || fIncludeZeroValue || (pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint() && GetCredit(pcoin->tx->vout[i], ISMINE_SPENDABLE) > 0)) &&\n                    (!coinControl || !coinControl->HasSelected() || coinControl->fAllowOtherInputs || coinControl->IsSelected(COutPoint((*it).first, i)))) {\n                        vCoins.push_back(COutput(pcoin, i, nDepth,\n                                                 ((mine & ISMINE_SPENDABLE) != ISMINE_NO) ||\n                                                  (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO),\n                                                 (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO));\n                }\n            }\n        }\n    }\n}\n\nvoid CWallet::AvailableCoinsForLMint(std::vector<std::pair<CAmount, std::vector<COutput>>>& valueAndUTXO, const CCoinControl *coinControl) const\n{\n    valueAndUTXO.clear();\n    std::vector<COutput> vAvailableCoins;\n    AvailableCoins(vAvailableCoins, true, coinControl);\n\n    std::map<CTxDestination, std::pair<CAmount, std::vector<COutput>>> mapAddrToUTXO;\n    for(const auto& coin : vAvailableCoins)\n    {\n        CTxDestination address;\n        const auto& scriptPubKey = coin.tx->tx->vout[coin.i].scriptPubKey;\n\n        if (!ExtractDestination(scriptPubKey, address) && !scriptPubKey.IsUnspendable())\n            continue;\n\n        auto& element = mapAddrToUTXO[address];\n        if(element.second.empty())\n            element.first = coin.tx->tx->vout[coin.i].nValue;\n        else\n            element.first += coin.tx->tx->vout[coin.i].nValue;\n        element.second.push_back(coin);\n    }\n\n    valueAndUTXO.reserve(mapAddrToUTXO.size());\n    for(const auto& element : mapAddrToUTXO)\n        valueAndUTXO.emplace_back(element.second);\n\n    std::sort(valueAndUTXO.begin(), valueAndUTXO.end(), [](const std::pair<CAmount,std::vector<COutput>> &left, const std::pair<CAmount,std::vector<COutput>> &right) {\n        return left.first > right.first;\n    });\n\n}\n\nbool CWallet::GetZnodeVinAndKeys(CTxIn &txinRet, CPubKey &pubKeyRet, CKey &keyRet, std::string strTxHash,\n                                 std::string strOutputIndex) {\n    // wait for reindex and/or import to finish\n    if (fImporting || fReindex) return false;\n\n    // Find possible candidates\n    std::vector <COutput> vPossibleCoins;\n    CCoinControl coinControl;\n    coinControl.nCoinType = CoinType::ONLY_1000;\n    AvailableCoins(vPossibleCoins, true, &coinControl, false);\n    if (vPossibleCoins.empty()) {\n        LogPrintf("CWallet::GetZnodeVinAndKeys -- Could not locate any valid znode vin\\n");\n        return false;\n    }\n\n    if (strTxHash.empty()) // No output specified, select the first one\n        return GetVinAndKeysFromOutput(vPossibleCoins[0], txinRet, pubKeyRet, keyRet);\n\n    // Find specific vin\n    uint256 txHash = uint256S(strTxHash);\n    int nOutputIndex = atoi(strOutputIndex.c_str());\n\n    BOOST_FOREACH(COutput & out, vPossibleCoins)\n    if (out.tx->GetHash() == txHash && out.i == nOutputIndex) // found it!\n        return GetVinAndKeysFromOutput(out, txinRet, pubKeyRet, keyRet);\n\n    LogPrintf("CWallet::GetZnodeVinAndKeys -- Could not locate specified znode vin\\n");\n    return false;\n}\n\nbool CWallet::GetVinAndKeysFromOutput(COutput out, CTxIn &txinRet, CPubKey &pubKeyRet, CKey &keyRet) {\n    // wait for reindex and/or import to finish\n    if (fImporting || fReindex) return false;\n\n    CScript pubScript;\n\n    txinRet = CTxIn(out.tx->GetHash(), out.i);\n    pubScript = out.tx->tx->vout[out.i].scriptPubKey; // the inputs PubKey\n\n    CTxDestination address1;\n    ExtractDestination(pubScript, address1);\n    CBitcoinAddress address2(address1);\n\n    CKeyID keyID;\n    if (!address2.GetKeyID(keyID)) {\n        LogPrintf("CWallet::GetVinAndKeysFromOutput -- Address does not refer to a key\\n");\n        return false;\n    }\n\n    if (!GetKey(keyID, keyRet)) {\n        LogPrintf("CWallet::GetVinAndKeysFromOutput -- Private key for address is not known\\n");\n        return false;\n    }\n\n    pubKeyRet = keyRet.GetPubKey();\n    return true;\n}\n\n//[firo]\nvoid CWallet::ListAvailableSigmaMintCoins(std::vector<COutput> &vCoins, bool fOnlyConfirmed) const {\n    EnsureMintWalletAvailable();\n\n    vCoins.clear();\n    LOCK2(cs_main, cs_wallet);\n    std::list<CSigmaEntry> listOwnCoins;\n    CWalletDB walletdb(pwalletMain->strWalletFile);\n    listOwnCoins = zwallet->GetTracker().MintsAsSigmaEntries(true, false);\n    LogPrintf("listOwnCoins.size()=%s\\n", listOwnCoins.size());\n    for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) {\n        const CWalletTx *pcoin = &(*it).second;\n//        LogPrintf("pcoin=%s\\n", pcoin->GetHash().ToString());\n        if (!CheckFinalTx(*pcoin)) {\n            LogPrintf("!CheckFinalTx(*pcoin)=%s\\n", !CheckFinalTx(*pcoin));\n            continue;\n        }\n\n        if (fOnlyConfirmed && !pcoin->IsTrusted()) {\n            LogPrintf("fOnlyConfirmed = %s, !pcoin->IsTrusted()\\n", fOnlyConfirmed, !pcoin->IsTrusted());\n            continue;\n        }\n\n        if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) {\n            LogPrintf("Not trusted\\n");\n            continue;\n        }\n\n        int nDepth = pcoin->GetDepthInMainChain();\n        if (nDepth < 0) {\n            LogPrintf("nDepth=%s\\n", nDepth);\n            continue;\n        }\n        LogPrintf("pcoin->tx->vout.size()=%s\\n", pcoin->tx->vout.size());\n\n        for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {\n            if (pcoin->tx->vout[i].scriptPubKey.IsSigmaMint()) {\n                CTxOut txout = pcoin->tx->vout[i];\n                secp_primitives::GroupElement pubCoin = sigma::ParseSigmaMintScript(\n                    txout.scriptPubKey);\n                LogPrintf("Pubcoin=%s\\n", pubCoin.tostring());\n                // CHECKING PROCESS\n                BOOST_FOREACH(const CSigmaEntry &ownCoinItem, listOwnCoins) {\n                   if (ownCoinItem.value == pubCoin && ownCoinItem.IsUsed == false &&\n                        ownCoinItem.randomness != uint64_t(0) && ownCoinItem.serialNumber != uint64_t(0)) {\n                        vCoins.push_back(COutput(pcoin, i, nDepth, true, true));\n                        LogPrintf("-->OK\\n");\n                    }\n                }\n            }\n        }\n    }\n}\n\nvoid CWallet::ListAvailableLelantusMintCoins(std::vector<COutput> &vCoins, bool fOnlyConfirmed) const {\n    EnsureMintWalletAvailable();\n\n    vCoins.clear();\n    LOCK2(cs_main, cs_wallet);\n    std::list<CLelantusEntry> listOwnCoins;\n    CWalletDB walletdb(pwalletMain->strWalletFile);\n    listOwnCoins = zwallet->GetTracker().MintsAsLelantusEntries(true, false);\n    LogPrintf("listOwnCoins.size()=%s\\n", listOwnCoins.size());\n    for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) {\n        const CWalletTx *pcoin = &(*it).second;\n//        LogPrintf("pcoin=%s\\n", pcoin->GetHash().ToString());\n        if (!CheckFinalTx(*pcoin)) {\n            LogPrintf("!CheckFinalTx(*pcoin)=%s\\n", !CheckFinalTx(*pcoin));\n            continue;\n        }\n\n        if (fOnlyConfirmed && !pcoin->IsTrusted()) {\n            LogPrintf("fOnlyConfirmed = %s, !pcoin->IsTrusted() = %s\\n", fOnlyConfirmed, !pcoin->IsTrusted());\n            continue;\n        }\n\n        if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) {\n            LogPrintf("Not trusted\\n");\n            continue;\n        }\n\n        int nDepth = pcoin->GetDepthInMainChain();\n        if (nDepth < 0) {\n            LogPrintf("nDepth=%s\\n", nDepth);\n            continue;\n        }\n        LogPrintf("pcoin->tx->vout.size()=%s\\n", pcoin->tx->vout.size());\n\n        for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++) {\n            if (pcoin->tx->vout[i].scriptPubKey.IsLelantusMint() || pcoin->tx->vout[i].scriptPubKey.IsLelantusJMint()) {\n                CTxOut txout = pcoin->tx->vout[i];\n                secp_primitives::GroupElement pubCoin;\n                try {\n                    lelantus::ParseLelantusMintScript(txout.scriptPubKey, pubCoin);\n                } catch (std::invalid_argument &) {\n                    continue;\n                }\n                LogPrintf("Pubcoin=%s\\n", pubCoin.tostring());\n                // CHECKING PROCESS\n                BOOST_FOREACH(const CLelantusEntry& ownCoinItem, listOwnCoins) {\n                    if (ownCoinItem.value == pubCoin && ownCoinItem.IsUsed == false &&\n                        !ownCoinItem.randomness.isZero() && !ownCoinItem.serialNumber.isZero()) {\n                        vCoins.push_back(COutput(pcoin, i, nDepth, true, true));\n                        LogPrintf("-->OK\\n");\n                    }\n                }\n            }\n        }\n    }\n}\n\nstatic void ApproximateBestSubset(std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > >vValue, const CAmount& nTotalLower, const CAmount& nTargetValue,\n                                  std::vector<char>& vfBest, CAmount& nBest, int iterations = 1000)\n{\n    std::vector<char> vfIncluded;\n\n    vfBest.assign(vValue.size(), true);\n    nBest = nTotalLower;\n\n    FastRandomContext insecure_rand;\n\n    for (int nRep = 0; nRep < iterations && nBest != nTargetValue; nRep++) {\n        vfIncluded.assign(vValue.size(), false);\n        CAmount nTotal = 0;\n        bool fReachedTarget = false;\n        for (int nPass = 0; nPass < 2 && !fReachedTarget; nPass++)\n        {\n            for (unsigned int i = 0; i < vValue.size(); i++)\n            {\n                //The solver here uses a randomized algorithm,\n                //the randomness serves no real security purpose but is just\n                //needed to prevent degenerate behavior and it is important\n                //that the rng is fast. We do not use a constant random sequence,\n                //because there may be some privacy improvement by making\n                //the selection random.\n                if (nPass == 0 ? insecure_rand.rand32()&1 : !vfIncluded[i])\n                {\n                    nTotal += vValue[i].first;\n                    vfIncluded[i] = true;\n                    if (nTotal >= nTargetValue)\n                    {\n                        fReachedTarget = true;\n                        if (nTotal < nBest)\n                        {\n                            nBest = nTotal;\n                            vfBest = vfIncluded;\n                        }\n                        nTotal -= vValue[i].first;\n                        vfIncluded[i] = false;\n                    }\n                }\n            }\n        }\n    }\n}\n\nbool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const int nConfMine, const int nConfTheirs, const uint64_t nMaxAncestors, std::vector<COutput> vCoins,\n                                 std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, bool fForUseInInstantSend) const\n{\n    setCoinsRet.clear();\n    nValueRet = 0;\n\n    // List of values less than target\n    std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > coinLowestLarger;\n    coinLowestLarger.first = std::numeric_limits<CAmount>::max();\n    coinLowestLarger.second.first = NULL;\n    std::vector<std::pair<CAmount, std::pair<const CWalletTx*,unsigned int> > > vValue;\n    CAmount nTotalLower = 0;\n\n    Shuffle(vCoins.begin(), vCoins.end(), FastRandomContext());\n\n    BOOST_FOREACH(const COutput &output, vCoins)\n    {\n        if (!output.fSpendable)\n            continue;\n\n        const CWalletTx *pcoin = output.tx;\n\n            bool fLockedByIS = pcoin->IsLockedByLLMQInstantSend();\n\n        if (output.nDepth < ((pcoin->IsFromMe(ISMINE_ALL) || pcoin->tx->IsLelantusMint()) ? nConfMine : nConfTheirs) && !fLockedByIS)\n            continue;\n\n        if (!mempool.TransactionWithinChainLimit(pcoin->GetHash(), nMaxAncestors))\n            continue;\n\n        int i = output.i;\n        CAmount n = pcoin->tx->vout[i].nValue;\n\n        std::pair<CAmount,std::pair<const CWalletTx*,unsigned int> > coin = std::make_pair(n,std::make_pair(pcoin, i));\n\n        if (n == nTargetValue)\n        {\n            setCoinsRet.insert(coin.second);\n            nValueRet += coin.first;\n            return true;\n        }\n        else if (n < nTargetValue + MIN_CHANGE)\n        {\n            vValue.push_back(coin);\n            nTotalLower += n;\n        }\n        else if (n < coinLowestLarger.first)\n        {\n            coinLowestLarger = coin;\n        }\n    }\n\n    if (nTotalLower == nTargetValue)\n    {\n        for (unsigned int i = 0; i < vValue.size(); ++i)\n        {\n            setCoinsRet.insert(vValue[i].second);\n            nValueRet += vValue[i].first;\n        }\n        return true;\n    }\n\n    if (nTotalLower < nTargetValue)\n    {\n        if (coinLowestLarger.second.first == NULL)\n            return false;\n        setCoinsRet.insert(coinLowestLarger.second);\n        nValueRet += coinLowestLarger.first;\n        return true;\n    }\n\n    // Solve subset sum by stochastic approximation\n    std::sort(vValue.begin(), vValue.end(), CompareValueOnly());\n    std::reverse(vValue.begin(), vValue.end());\n    std::vector<char> vfBest;\n    CAmount nBest;\n\n    ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest);\n    if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE)\n        ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);\n\n    // If we have a bigger coin and (either the stochastic approximation didn\'t find a good solution,\n    //                                   or the next bigger coin is closer), return the bigger coin\n    if (coinLowestLarger.second.first &&\n        ((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest))\n    {\n        setCoinsRet.insert(coinLowestLarger.second);\n        nValueRet += coinLowestLarger.first;\n    }\n    else {\n        for (unsigned int i = 0; i < vValue.size(); i++)\n            if (vfBest[i])\n            {\n                setCoinsRet.insert(vValue[i].second);\n                nValueRet += vValue[i].first;\n            }\n\n        LogPrint("selectcoins", "SelectCoins() best subset: ");\n        for (unsigned int i = 0; i < vValue.size(); i++)\n            if (vfBest[i])\n                LogPrint("selectcoins", "%s ", FormatMoney(vValue[i].first));\n        LogPrint("selectcoins", "total %s\\n", FormatMoney(nBest));\n    }\n\n    return true;\n}\n\nbool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl* coinControl, bool fForUseInInstantSend) const\n{\n    std::vector<COutput> vCoins(vAvailableCoins);\n    CoinType nCoinType = coinControl ? coinControl->nCoinType : CoinType::ALL_COINS;\n\n    // coin control -> return all selected outputs (we want all selected to go into the transaction for sure)\n    if (coinControl && coinControl->HasSelected() && !coinControl->fAllowOtherInputs)\n    {\n        BOOST_FOREACH(const COutput& out, vCoins)\n        {\n            if (!out.fSpendable)\n                 continue;\n            nValueRet += out.tx->tx->vout[out.i].nValue;\n            setCoinsRet.insert(std::make_pair(out.tx, out.i));\n        }\n        return (nValueRet >= nTargetValue);\n    }\n\n    // calculate value from preset inputs and store them\n    std::set<std::pair<const CWalletTx*, uint32_t> > setPresetCoins;\n    CAmount nValueFromPresetInputs = 0;\n\n    std::vector<COutPoint> vPresetInputs;\n    if (coinControl)\n        coinControl->ListSelected(vPresetInputs);\n    BOOST_FOREACH(const COutPoint& outpoint, vPresetInputs)\n    {\n        std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);\n        if (it != mapWallet.end())\n        {\n            const CWalletTx* pcoin = &it->second;\n            // Clearly invalid input, fail\n            if (pcoin->tx->vout.size() <= outpoint.n)\n                return false;\n            nValueFromPresetInputs += pcoin->tx->vout[outpoint.n].nValue;\n            setPresetCoins.insert(std::make_pair(pcoin, outpoint.n));\n        } else\n            return false; // TODO: Allow non-wallet inputs\n    }\n\n    // remove preset inputs from vCoins\n    for (std::vector<COutput>::iterator it = vCoins.begin(); it != vCoins.end() && coinControl && coinControl->HasSelected();)\n    {\n        if (setPresetCoins.count(std::make_pair(it->tx, it->i)))\n            it = vCoins.erase(it);\n        else\n            ++it;\n    }\n\n    size_t nMaxChainLength = std::min(GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT), GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT));\n    bool fRejectLongChains = GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);\n\n    bool res = nTargetValue <= nValueFromPresetInputs ||\n        SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 6, 0, vCoins, setCoinsRet, nValueRet, fForUseInInstantSend) ||\n        SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 1, 1, 0, vCoins, setCoinsRet, nValueRet, fForUseInInstantSend) ||\n        (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, 2, vCoins, setCoinsRet, nValueRet, fForUseInInstantSend)) ||\n        (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::min((size_t)4, nMaxChainLength/3), vCoins, setCoinsRet, nValueRet, fForUseInInstantSend)) ||\n        (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength/2, vCoins, setCoinsRet, nValueRet, fForUseInInstantSend)) ||\n        (bSpendZeroConfChange && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, nMaxChainLength, vCoins, setCoinsRet, nValueRet, fForUseInInstantSend)) ||\n        (bSpendZeroConfChange && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, 0, 1, std::numeric_limits<uint64_t>::max(), vCoins, setCoinsRet, nValueRet, fForUseInInstantSend));\n\n    // because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset\n    setCoinsRet.insert(setPresetCoins.begin(), setPresetCoins.end());\n\n    // add preset inputs to the total value selected\n    nValueRet += nValueFromPresetInputs;\n\n    return res;\n}\n\nbool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool overrideEstimatedFeeRate, const CFeeRate& specificFeeRate, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, bool keepReserveKey, const CTxDestination& destChange)\n{\n    std::vector<CRecipient> vecSend;\n\n    // Turn the txout set into a CRecipient vector\n    for (size_t idx = 0; idx < tx.vout.size(); idx++)\n    {\n        const CTxOut& txOut = tx.vout[idx];\n        CRecipient recipient = {txOut.scriptPubKey, txOut.nValue, setSubtractFeeFromOutputs.count(idx) == 1};\n        vecSend.push_back(recipient);\n    }\n\n    CCoinControl coinControl;\n    coinControl.destChange = destChange;\n    coinControl.fAllowOtherInputs = true;\n    coinControl.fAllowWatchOnly = includeWatching;\n    coinControl.fOverrideFeeRate = overrideEstimatedFeeRate;\n    coinControl.nFeeRate = specificFeeRate;\n\n    BOOST_FOREACH(const CTxIn& txin, tx.vin)\n        coinControl.Select(txin.prevout);\n\n    int nExtraPayloadSize = 0;\n    if (tx.nVersion == 3 && tx.nType != TRANSACTION_NORMAL)\n        nExtraPayloadSize = (int)tx.vExtraPayload.size();\n\n    CReserveKey reservekey(this);\n    CWalletTx wtx;\n    if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, nExtraPayloadSize))\n        return false;\n\n    if (nChangePosInOut != -1)\n        tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.tx->vout[nChangePosInOut]);\n\n    // Copy output sizes from new transaction; they may have had the fee subtracted from them\n    for (unsigned int idx = 0; idx < tx.vout.size(); idx++)\n        tx.vout[idx].nValue = wtx.tx->vout[idx].nValue;\n\n    // Add new txins (keeping original txin scriptSig/order)\n    BOOST_FOREACH(const CTxIn& txin, wtx.tx->vin)\n    {\n        if (!coinControl.IsSelected(txin.prevout))\n        {\n            tx.vin.push_back(txin);\n\n            if (lockUnspents)\n            {\n              LOCK2(cs_main, cs_wallet);\n              LockCoin(txin.prevout);\n            }\n        }\n    }\n\n    // optionally keep the change output key\n    if (keepReserveKey)\n        reservekey.KeepKey();\n\n    return true;\n}\n\nbool CWallet::ConvertList(std::vector <CTxIn> vecTxIn, std::vector <CAmount> &vecAmounts) {\n    BOOST_FOREACH(CTxIn txin, vecTxIn) {\n        if (mapWallet.count(txin.prevout.hash)) {\n            CWalletTx &wtx = mapWallet[txin.prevout.hash];\n            if (txin.prevout.n < wtx.tx->vout.size()) {\n                vecAmounts.push_back(wtx.tx->vout[txin.prevout.n].nValue);\n            }\n        } else {\n            LogPrintf("CWallet::ConvertList -- Couldn\'t find transaction\\n");\n        }\n    }\n    return true;\n}\n\nbool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,\n                                int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign, int nExtraPayloadSize, bool fUseInstantSend)\n{\n    CAmount nFeePay = 0;\n\n    CAmount nValue = 0;\n    int nChangePosRequest = nChangePosInOut;\n    unsigned int nSubtractFeeFromAmount = 0;\n    for (const auto& recipient : vecSend)\n    {\n        if (nValue < 0 || recipient.nAmount < 0)\n        {\n            strFailReason = _("Transaction amounts must not be negative");\n            return false;\n        }\n        nValue += recipient.nAmount;\n\n        if (recipient.fSubtractFeeFromAmount)\n            nSubtractFeeFromAmount++;\n    }\n    if (vecSend.empty())\n    {\n        strFailReason = _("Transaction must have at least one recipient");\n        return false;\n    }\n\n    wtxNew.fTimeReceivedIsTxTime = true;\n    wtxNew.BindWallet(this);\n    CMutableTransaction txNew;\n\n    // Discourage fee sniping.\n    //\n    // For a large miner the value of the transactions in the best block and\n    // the mempool can exceed the cost of deliberately attempting to mine two\n    // blocks to orphan the current best block. By setting nLockTime such that\n    // only the next block can include the transaction, we discourage this\n    // practice as the height restricted and limited blocksize gives miners\n    // considering fee sniping fewer options for pulling off this attack.\n    //\n    // A simple way to think about this is from the wallet\'s point of view we\n    // always want the blockchain to move forward. By setting nLockTime this\n    // way we\'re basically making the statement that we only want this\n    // transaction to appear in the next block; we don\'t want to potentially\n    // encourage reorgs by allowing transactions to appear at lower heights\n    // than the next block in forks of the best chain.\n    //\n    // Of course, the subsidy is high enough, and transaction volume low\n    // enough, that fee sniping isn\'t a problem yet, but by implementing a fix\n    // now we ensure code won\'t be written that makes assumptions about\n    // nLockTime that preclude a fix later.\n\n    txNew.nLockTime = chainActive.Height();\n\n    // Secondly occasionally randomly pick a nLockTime even further back, so\n    // that transactions that are delayed after signing for whatever reason,\n    // e.g. high-latency mix networks and some CoinJoin implementations, have\n    // better privacy.\n    if (GetRandInt(10) == 0)\n        txNew.nLockTime = std::max(0, (int)txNew.nLockTime - GetRandInt(100));\n\n    assert(txNew.nLockTime <= (unsigned int)chainActive.Height());\n    assert(txNew.nLockTime < LOCKTIME_THRESHOLD);\n\n    {\n        std::set<std::pair<const CWalletTx*, unsigned int>> setCoins;\n        LOCK2(cs_main, cs_wallet);\n        {\n            std::vector<COutput> vAvailableCoins;\n            AvailableCoins(vAvailableCoins, true, coinControl, false, fUseInstantSend);\n            int nInstantSendConfirmationsRequired = Params().GetConsensus().nInstantSendConfirmationsRequired;\n\n            nFeeRet = 0;\n            if(nFeePay > 0) nFeeRet = nFeePay;\n            double dPriority = 0;\n            // Start with no fee and loop until there is enough fee\n            while (true)\n            {\n                nChangePosInOut = nChangePosRequest;\n                txNew.vin.clear();\n                txNew.vout.clear();\n                wtxNew.fFromMe = true;\n                bool fFirst = true;\n\n                CAmount nValueToSelect = nValue;\n                if (nSubtractFeeFromAmount == 0)\n                    nValueToSelect += nFeeRet;\n                // vouts to the payees\n                for (const auto& recipient : vecSend)\n                {\n                    CTxOut txout(recipient.nAmount, recipient.scriptPubKey);\n\n                    if (recipient.fSubtractFeeFromAmount)\n                    {\n                        txout.nValue -= nFeeRet / nSubtractFeeFromAmount; // Subtract fee equally from each selected recipient\n\n                        if (fFirst) // first receiver pays the remainder not divisible by output count\n                        {\n                            fFirst = false;\n                            txout.nValue -= nFeeRet % nSubtractFeeFromAmount;\n                        }\n                    }\n\n                    if (txout.IsDust(dustRelayFee))\n                    {\n                        if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)\n                        {\n                            if (txout.nValue < 0)\n                                strFailReason = _("The transaction amount is too small to pay the fee");\n                            else\n                                strFailReason = _("The transaction amount is too small to send after the fee has been deducted");\n                        }\n                        else\n                            strFailReason = _("Transaction amount too small");\n                        return false;\n                    }\n                    txNew.vout.push_back(txout);\n                }\n\n                // Choose coins to use\n                CAmount nValueIn = 0;\n                setCoins.clear();\n                if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl, fUseInstantSend))\n                {\n                    strFailReason = _("Insufficient funds");\n                    return false;\n                }\n\n                const CAmount nChange = nValueIn - nValueToSelect;\n                CTxOut newTxOut;\n\n                if (nChange > 0)\n                {\n                    // Fill a vout to ourself\n                    // TODO: pass in scriptChange instead of reservekey so\n                    // change transaction isn\'t always pay-to-dash-address\n                    CScript scriptChange;\n\n                    // coin control: send change to custom address\n                    if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))\n                        scriptChange = GetScriptForDestination(coinControl->destChange);\n\n                    // no coin control: send change to newly generated address\n                    else\n                    {\n                        // Note: We use a new key here to keep it from being obvious which side is the change.\n                        //  The drawback is that by not reusing a previous key, the change may be lost if a\n                        //  backup is restored, if the backup doesn\'t have the new private key for the change.\n                        //  If we reused the old key, it would be possible to add code to look for and\n                        //  rediscover unknown transactions that were written with keys of ours to recover\n                        //  post-backup change.\n\n                        // Reserve a new key pair from key pool\n                        CPubKey vchPubKey;\n                        bool ret;\n                        ret = reservekey.GetReservedKey(vchPubKey);\n                        if (!ret)\n                        {\n                            strFailReason = _("Keypool ran out, please call keypoolrefill first");\n                            return false;\n                        }\n\n                        scriptChange = GetScriptForDestination(vchPubKey.GetID());\n                    }\n\n                    newTxOut = CTxOut(nChange, scriptChange);\n\n                    // We do not move dust-change to fees, because the sender would end up paying more than requested.\n                    // This would be against the purpose of the all-inclusive feature.\n                    // So instead we raise the change and deduct from the recipient.\n                    if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))\n                    {\n                        CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;\n                        newTxOut.nValue += nDust; // raise change until no more dust\n                        for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient\n                        {\n                            if (vecSend[i].fSubtractFeeFromAmount)\n                            {\n                                txNew.vout[i].nValue -= nDust;\n                                if (txNew.vout[i].IsDust(dustRelayFee))\n                                {\n                                    strFailReason = _("The transaction amount is too small to send after the fee has been deducted");\n                                    return false;\n                                }\n                                break;\n                            }\n                        }\n                    }\n\n                    // Never create dust outputs; if we would, just\n                    // add the dust to the fee.\n                    if (newTxOut.IsDust(dustRelayFee))\n                    {\n                        nChangePosInOut = -1;\n                        nFeeRet += nChange;\n                        reservekey.ReturnKey();\n                    }\n                    else\n                    {\n                        if (nChangePosInOut == -1)\n                        {\n                            // Insert change txn at random position:\n                            nChangePosInOut = GetRandInt(txNew.vout.size()+1);\n                        }\n                        else if ((unsigned int)nChangePosInOut > txNew.vout.size())\n                        {\n                            strFailReason = _("Change index out of range");\n                            return false;\n                        }\n\n                        std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;\n                        txNew.vout.insert(position, newTxOut);\n                    }\n                } else {\n                    reservekey.ReturnKey();\n                    nChangePosInOut = -1;\n                }\n\n                // Fill vin\n                //\n                // Note how the sequence number is set to max()-1 so that the\n                // nLockTime set above actually works.\n                //\n                // BIP125 defines opt-in RBF as any nSequence < maxint-1, so\n                // we use the highest possible value in that range (maxint-2)\n                // to avoid conflicting with other possible uses of nSequence,\n                // and in the spirit of "smallest possible change from prior\n                // behavior."\n                for (const auto& coin : setCoins)\n                    txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),\n                                              std::numeric_limits<unsigned int>::max() - (fWalletRbf ? 2 : 1)));\n\n                // Fill in dummy signatures for fee calculation.\n                if (!DummySignTx(txNew, setCoins)) {\n                    strFailReason = _("Signing transaction failed");\n                    return false;\n                }\n\n                unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION);\n\n                if (nExtraPayloadSize != 0) {\n                    // account for extra payload in fee calculation\n                    nBytes += GetSizeOfCompactSize(nExtraPayloadSize) + nExtraPayloadSize;\n                }\n\n                if (nBytes > MAX_STANDARD_TX_SIZE) {\n                    // Do not create oversized transactions (bad-txns-oversize).\n                    strFailReason = _("Transaction too large");\n                    return false;\n                }\n\n                CTransaction txNewConst(txNew);\n                dPriority = txNewConst.ComputePriority(dPriority, nBytes);\n\n                // Remove scriptSigs to eliminate the fee calculation dummy signatures\n                for (auto& vin : txNew.vin) {\n                    vin.scriptSig = CScript();\n                    vin.scriptWitness.SetNull();\n                }\n\n                // Allow to override the default confirmation target over the CoinControl instance\n                int currentConfirmationTarget = nTxConfirmTarget;\n                if (coinControl && coinControl->nConfirmTarget > 0)\n                    currentConfirmationTarget = coinControl->nConfirmTarget;\n\n                // Can we complete this as a free transaction?\n                if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)\n                {\n                    // Not enough fee: enough priority?\n                    double dPriorityNeeded = mempool.estimateSmartPriority(currentConfirmationTarget);\n                    // Require at least hard-coded AllowFree.\n                    if (dPriority >= dPriorityNeeded && AllowFree(dPriority))\n                        break;\n                }\n\n                CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, mempool);\n                if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {\n                    nFeeNeeded = coinControl->nMinimumTotalFee;\n                }\n                if (coinControl && coinControl->fOverrideFeeRate)\n                    nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);\n\n                // If we made it here and we aren\'t even able to meet the relay fee on the next pass, give up\n                // because we must be at the maximum allowed fee.\n                if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))\n                {\n                    strFailReason = _("Transaction too large for fee policy");\n                    return false;\n                }\n\n                if (nFeeRet >= nFeeNeeded) {\n                    // Reduce fee to only the needed amount if we have change\n                    // output to increase.  This prevents potential overpayment\n                    // in fees if the coins selected to meet nFeeNeeded result\n                    // in a transaction that requires less fee than the prior\n                    // iteration.\n                    // TODO: The case where nSubtractFeeFromAmount > 0 remains\n                    // to be addressed because it requires returning the fee to\n                    // the payees and not the change output.\n                    // TODO: The case where there is no change output remains\n                    // to be addressed so we avoid creating too small an output.\n                    if (nFeeRet > nFeeNeeded && nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {\n                        CAmount extraFeePaid = nFeeRet - nFeeNeeded;\n                        std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;\n                        change_position->nValue += extraFeePaid;\n                        nFeeRet -= extraFeePaid;\n                    }\n                    break; // Done, enough fee included.\n                }\n\n                // Try to reduce change to include necessary fee\n                if (nChangePosInOut != -1 && nSubtractFeeFromAmount == 0) {\n                    CAmount additionalFeeNeeded = nFeeNeeded - nFeeRet;\n                    std::vector<CTxOut>::iterator change_position = txNew.vout.begin()+nChangePosInOut;\n                    // Only reduce change if remaining amount is still a large enough output.\n                    if (change_position->nValue >= MIN_FINAL_CHANGE + additionalFeeNeeded) {\n                        change_position->nValue -= additionalFeeNeeded;\n                        nFeeRet += additionalFeeNeeded;\n                        break; // Done, able to increase fee from change\n                    }\n                }\n\n                // Include more fee and try again.\n                nFeeRet = nFeeNeeded;\n                continue;\n            }\n        }\n\n        if (sign)\n        {\n            CTransaction txNewConst(txNew);\n            int nIn = 0;\n            for (const auto& coin : setCoins)\n            {\n                const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;\n                SignatureData sigdata;\n\n                if (!ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata))\n                {\n                    strFailReason = _("Signing transaction failed");\n                    return false;\n                } else {\n                    UpdateTransaction(txNew, nIn, sigdata);\n                }\n\n                nIn++;\n            }\n        }\n\n        // Embed the constructed transaction data in wtxNew.\n        wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));\n    }\n\n    if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {\n        // Lastly, ensure this tx will pass the mempool\'s chain limits\n        LockPoints lp;\n        CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, false, 0, lp);\n\n        CTxMemPool::setEntries setAncestors;\n        size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);\n        size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;\n        size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);\n        size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;\n        std::string errString;\n        if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {\n            strFailReason = _("Transaction has too long of a mempool chain");\n            return false;\n        }\n    }\n    return true;\n}\n\n/**\n * Call after CreateTransaction unless you want to abort\n */\nbool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey, CConnman* connman, CValidationState& state)\n{\n    {\n        LOCK2(cs_main, cs_wallet);\n        LogPrintf("CommitTransaction:\\n%s", wtxNew.tx->ToString());\n        {\n            // Take key pair from key pool so it won\'t be used again\n            reservekey.KeepKey();\n\n            // Add tx to wallet, because if it has change it\'s also ours,\n            // otherwise just for transaction history.\n            AddToWallet(wtxNew);\n\n            // Notify that old coins are spent\n            BOOST_FOREACH(const CTxIn& txin, wtxNew.tx->vin)\n            {\n                // Skip inputs of anonymized transactions\n                if (txin.prevout.hash.IsNull())\n                    continue;\n\n                CWalletTx &coin = mapWallet[txin.prevout.hash];\n                coin.BindWallet(this);\n                NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);\n            }\n        }\n\n        // Track how many getdata requests our transaction gets\n        mapRequestCount[wtxNew.GetHash()] = 0;\n\n        if (fBroadcastTransactions)\n        {\n            // Broadcast\n            if (!wtxNew.AcceptToMemoryPool(maxTxFee, state)) {\n                LogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\\n", state.GetRejectReason());\n                // TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.\n            } else {\n                wtxNew.RelayWalletTransaction(connman);\n            }\n        }\n    }\n    return true;\n}\n\nbool CWallet::EraseFromWallet(uint256 hash) {\n    if (!fFileBacked)\n        return false;\n    {\n        LOCK(cs_wallet);\n        if (mapWallet.erase(hash))\n            CWalletDB(strWalletFile).EraseTx(hash);\n    }\n    return true;\n}\n\n/**\n * @brief CWallet::CreateMintTransaction\n * @param vecSend\n * @param wtxNew\n * @param reservekey\n * @param nFeeRet\n * @param strFailReason\n * @param coinControl\n * @return\n */\nbool CWallet::CreateMintTransaction(const std::vector <CRecipient> &vecSend, CWalletTx &wtxNew,\n                                            CReserveKey &reservekey,\n                                            CAmount &nFeeRet, int &nChangePosInOut, std::string &strFailReason,\n                                            const CCoinControl *coinControl, bool sign) {\n    CAmount nValue = 0;\n    int nChangePosRequest = nChangePosInOut;\n    unsigned int nSubtractFeeFromAmount = 0;\n    BOOST_FOREACH(const CRecipient &recipient, vecSend)\n    {\n        if (nValue < 0 || recipient.nAmount < 0) {\n            strFailReason = _("Transaction amounts must be positive");\n            return false;\n        }\n        nValue += recipient.nAmount;\n    }\n    if (vecSend.empty() || nValue < 0) {\n        strFailReason = _("Transaction amounts must be positive");\n        return false;\n    }\n    wtxNew.fTimeReceivedIsTxTime = true;\n    wtxNew.BindWallet(this);\n    CMutableTransaction txNew;\n    txNew.nLockTime = chainActive.Height();\n    if (GetRandInt(10) == 0)\n        txNew.nLockTime = std::max(0, (int) txNew.nLockTime - GetRandInt(100));\n\n    assert(txNew.nLockTime <= (unsigned int) chainActive.Height());\n    assert(txNew.nLockTime < LOCKTIME_THRESHOLD);\n\n    {\n        LOCK2(cs_main, cs_wallet);\n        {\n            std::vector<COutput> vAvailableCoins;\n            AvailableCoins(vAvailableCoins, true, coinControl);\n\n            nFeeRet = payTxFee.GetFeePerK();\n            LogPrintf("nFeeRet=%s\\n", nFeeRet);\n            // Start with no fee and loop until there is enough fee\n            while (true)\n            {\n                nChangePosInOut = nChangePosRequest;\n                txNew.vin.clear();\n                txNew.vout.clear();\n\n                wtxNew.fFromMe = true;\n                wtxNew.changes.clear();\n\n                CAmount nValueToSelect = nValue + nFeeRet;\n                double dPriority = 0;\n                // vouts to the payees\n                BOOST_FOREACH(const CRecipient &recipient, vecSend)\n                {\n                    CTxOut txout(recipient.nAmount, recipient.scriptPubKey);\n                    LogPrintf("txout:%s\\n", txout.ToString());\n\n                    if (txout.IsDust(::minRelayTxFee)) {\n                        if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) {\n                            if (txout.nValue < 0)\n                                strFailReason = _("The transaction amount is too small to pay the fee");\n                            else\n                                strFailReason = _(\n                                        "The transaction amount is too small to send after the fee has been deducted");\n                        } else\n                            strFailReason = _("Transaction amount too small");\n                        return false;\n                    }\n                    txNew.vout.push_back(txout);\n                }\n\n                // Choose coins to use\n                std::set<std::pair<const CWalletTx*,unsigned int> > setCoins;\n                CAmount nValueIn = 0;\n                if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coinControl))\n                {\n                    if (nValueIn < nValueToSelect) {\n                        strFailReason = _("Insufficient funds");\n                    }\n                    return false;\n                }\n                BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)\n                {\n                    CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;\n                    //The coin age after the next block (depth+1) is used instead of the current,\n                    //reflecting an assumption the user would accept a bit more delay for\n                    //a chance at a free transaction.\n                    //But mempool inputs might still be in the mempool, so their age stays 0\n                    int age = pcoin.first->GetDepthInMainChain();\n                    assert(age >= 0);\n                    if (age != 0)\n                        age += 1;\n                    dPriority += (double)nCredit * age;\n                }\n\n                CAmount nChange = nValueIn - nValueToSelect;\n\n                if (nChange > 0) {\n                    // Fill a vout to ourself\n                    // TODO: pass in scriptChange instead of reservekey so\n                    // change transaction isn\'t always pay-to-bitcoin-address\n                    CScript scriptChange;\n\n                    // coin control: send change to custom address\n                    if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))\n                        scriptChange = GetScriptForDestination(coinControl->destChange);\n\n                    // send change to one of the specified change addresses\n                    else if (IsArgSet("-change") && mapMultiArgs.at("-change").size() > 0) {\n                        CBitcoinAddress address(mapMultiArgs.at("change")[GetRandInt(mapMultiArgs.at("-change").size())]);\n                        CKeyID keyID;\n                        if (!address.GetKeyID(keyID)) {\n                            strFailReason = _("Bad change address");\n                            return false;\n                        }\n                        scriptChange = GetScriptForDestination(keyID);\n                    }\n\n                    // no coin control: send change to newly generated address\n                    else\n                    {\n                        // Note: We use a new key here to keep it from being obvious which side is the change.\n                        //  The drawback is that by not reusing a previous key, the change may be lost if a\n                        //  backup is restored, if the backup doesn\'t have the new private key for the change.\n                        //  If we reused the old key, it would be possible to add code to look for and\n                        //  rediscover unknown transactions that were written with keys of ours to recover\n                        //  post-backup change.\n\n                        // Reserve a new key pair from key pool\n                        CPubKey vchPubKey;\n                        bool ret;\n                        ret = reservekey.GetReservedKey(vchPubKey);\n                        if (!ret)\n                        {\n                            strFailReason = _("Keypool ran out, please call keypoolrefill first");\n                            return false;\n                        }\n\n                        scriptChange = GetScriptForDestination(vchPubKey.GetID());\n                    }\n\n                    CTxOut newTxOut(nChange, scriptChange);\n\n                    // We do not move dust-change to fees, because the sender would end up paying more than requested.\n                    // This would be against the purpose of the all-inclusive feature.\n                    // So instead we raise the change and deduct from the recipient.\n                    if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(::minRelayTxFee))\n                    {\n                        CAmount nDust = newTxOut.GetDustThreshold(::minRelayTxFee) - newTxOut.nValue;\n                        newTxOut.nValue += nDust; // raise change until no more dust\n                        for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient\n                        {\n                            if (vecSend[i].fSubtractFeeFromAmount)\n                            {\n                                txNew.vout[i].nValue -= nDust;\n                                if (txNew.vout[i].IsDust(::minRelayTxFee))\n                                {\n                                    strFailReason = _("The transaction amount is too small to send after the fee has been deducted");\n                                    return false;\n                                }\n                                break;\n                            }\n                        }\n                    }\n\n                    // Never create dust outputs; if we would, just\n                    // add the dust to the fee.\n                    if (newTxOut.IsDust(::minRelayTxFee))\n                    {\n                        nChangePosInOut = -1;\n                        nFeeRet += nChange;\n                        reservekey.ReturnKey();\n                    }\n                    else\n                    {\n                        if (nChangePosInOut == -1)\n                        {\n                            // Insert change txn at random position:\n                            nChangePosInOut = GetRandInt(txNew.vout.size()+1);\n                        }\n                        else if ((unsigned int)nChangePosInOut > txNew.vout.size())\n                        {\n                            strFailReason = _("Change index out of range");\n                            return false;\n                        }\n\n                        std::vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;\n                        txNew.vout.insert(position, newTxOut);\n                        wtxNew.changes.insert(static_cast<uint32_t>(nChangePosInOut));\n                    }\n                }\n                else\n                    reservekey.ReturnKey();\n\n                // Fill vin\n                //\n                // Note how the sequence number is set to max()-1 so that the\n                // nLockTime set above actually works.\n                BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)\n                    txNew.vin.push_back(CTxIn(coin.first->GetHash(),coin.second,CScript(),\n                                              std::numeric_limits<unsigned int>::max()-1));\n\n                // Sign\n                int nIn = 0;\n                CTransaction txNewConst(txNew);\n                BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins)\n                {\n                    bool signSuccess;\n                    const CScript& scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;\n                    SignatureData sigdata;\n                    if (sign)\n                        signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn, coin.first->tx->vout[coin.second].nValue, SIGHASH_ALL), scriptPubKey, sigdata);\n                    else\n                        signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);\n\n                    if (!signSuccess)\n                    {\n                        strFailReason = _("Signing transaction failed");\n                        return false;\n                    } else {\n                        UpdateTransaction(txNew, nIn, sigdata);\n                    }\n                    nIn++;\n                }\n                unsigned int nBytes = GetVirtualTransactionSize(txNew);\n                // Remove scriptSigs if we used dummy signatures for fee calculation\n                if (!sign) {\n                    BOOST_FOREACH(CTxIn & vin, txNew.vin)\n                    vin.scriptSig = CScript();\n                }\n                // Embed the constructed transaction data in wtxNew.\n                wtxNew.SetTx(MakeTransactionRef(std::move(txNew)));\n\n                // Limit size\n                if (GetTransactionWeight(*wtxNew.tx) >= MAX_STANDARD_TX_WEIGHT) {\n                    strFailReason = _("Transaction too large");\n                    return false;\n                }\n                dPriority = wtxNew.tx->ComputePriority(dPriority, nBytes);\n\n                // Can we complete this as a free transaction?\n                if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) {\n                    // Not enough fee: enough priority?\n                    double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget);\n                    // Require at least hard-coded AllowFree.\n                    if (dPriority >= dPriorityNeeded && AllowFree(dPriority))\n                        break;\n                }\n                CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);\n                if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {\n                    nFeeNeeded = coinControl->nMinimumTotalFee;\n                }\n                if (coinControl && coinControl->fOverrideFeeRate)\n                    nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);\n                if (nFeeRet >= nFeeNeeded)\n                    break; // Done, enough fee included.\n\n                // Include more fee and try again.\n                nFeeRet = nFeeNeeded;\n                continue;\n            }\n        }\n    }\n\n    if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {\n        // Lastly, ensure this tx will pass the mempool\'s chain limits\n        LockPoints lp;\n        CTxMemPoolEntry entry(MakeTransactionRef(txNew), 0, 0, 0, 0, false, 0, lp);\n        CTxMemPool::setEntries setAncestors;\n        size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);\n        size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;\n        size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);\n        size_t nLimitDescendantSize = GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;\n        std::string errString;\n        if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize,\n                                               nLimitDescendants, nLimitDescendantSize, errString)) {\n            strFailReason = _("Transaction has too long of a mempool chain");\n            return false;\n        }\n    }\n    return true;\n}\n\nbool CWallet::CreateLelantusMintTransactions(\n    CAmount valueToMint,\n    std::vector<std::pair<CWalletTx, CAmount>>& wtxAndFee,\n    CAmount& nAllFeeRet,\n    std::vector<CHDMint>& dMints,\n    std::list<CReserveKey>& reservekeys,\n    int& nChangePosInOut,\n    std::string& strFailReason,\n    const CCoinControl *coinControl,\n    bool autoMintAll,\n    bool sign)\n{\n    const auto& lelantusParams = lelantus::Params::get_default();\n\n    int nChangePosRequest = nChangePosInOut;\n\n    // Create transaction template\n    CWalletTx wtxNew;\n    wtxNew.fTimeReceivedIsTxTime = true;\n    wtxNew.BindWallet(this);\n\n    CMutableTransaction txNew;\n    txNew.nLockTime = chainActive.Height();\n\n    assert(txNew.nLockTime <= (unsigned int) chainActive.Height());\n    assert(txNew.nLockTime < LOCKTIME_THRESHOLD);\n\n    {\n        LOCK2(cs_main, cs_wallet);\n        {\n            std::list<CWalletTx> cacheWtxs;\n            std::vector<std::pair<CAmount, std::vector<COutput>>> valueAndUTXO;\n            AvailableCoinsForLMint(valueAndUTXO, coinControl);\n\n            Shuffle(valueAndUTXO.begin(), valueAndUTXO.end(), FastRandomContext());\n            {\n                CWalletDB walletdb(pwalletMain->strWalletFile);\n                pwalletMain->zwallet->ResetCount(walletdb);\n            }\n            while (!valueAndUTXO.empty()) {\n\n                // initialize\n                CWalletTx wtx = wtxNew;\n                CMutableTransaction tx = txNew;\n\n                reservekeys.emplace_back(this);\n                auto &reservekey = reservekeys.back();\n\n                if (GetRandInt(10) == 0)\n                    tx.nLockTime = std::max(0, (int) tx.nLockTime - GetRandInt(100));\n\n                CHDMint dMint;\n\n                auto nFeeRet = 0;\n                LogPrintf("nFeeRet=%s\\n", nFeeRet);\n\n                auto itr = valueAndUTXO.begin();\n\n                CAmount valueToMintInTx = std::min(\n                        ::Params().GetConsensus().nMaxValueLelantusMint,\n                        itr->first);\n\n                if (!autoMintAll) {\n                    valueToMintInTx = std::min(valueToMintInTx, valueToMint);\n                }\n\n                CAmount nValueToSelect, mintedValue;\n\n                std::set<std::pair<const CWalletTx *, unsigned int>> setCoins;\n                bool skipCoin = false;\n                // Start with no fee and loop until there is enough fee\n                while (true) {\n                    mintedValue = valueToMintInTx;\n                    nValueToSelect = mintedValue + nFeeRet;\n\n                    // if have no enough coins in this group then subtract fee from mint\n                    if (nValueToSelect > itr->first) {\n                        mintedValue -= nFeeRet;\n                        nValueToSelect = mintedValue + nFeeRet;\n                    }\n\n                    if (!MoneyRange(mintedValue) || mintedValue == 0) {\n                        valueAndUTXO.erase(itr);\n                        skipCoin = true;\n                        break;\n                    }\n\n                    nChangePosInOut = nChangePosRequest;\n                    tx.vin.clear();\n                    tx.vout.clear();\n\n                    wtx.fFromMe = true;\n                    wtx.changes.clear();\n\n                    setCoins.clear();\n\n                    // create recipient using random private coin to mock script sig\n                    lelantus::PrivateCoin privCoin(lelantusParams, mintedValue);\n                    auto recipient = CWallet::CreateLelantusMintRecipient(privCoin, dMint, false);\n\n                    double dPriority = 0;\n\n                    // vout to create mint\n                    CTxOut txout(recipient.nAmount, recipient.scriptPubKey);\n\n                    if (txout.IsDust(::minRelayTxFee)) {\n                        strFailReason = _("Transaction amount too small");\n                        return false;\n                    }\n\n                    tx.vout.push_back(txout);\n\n                    // Choose coins to use\n\n                    CAmount nValueIn = 0;\n                    if (!SelectCoins(itr->second, nValueToSelect, setCoins, nValueIn, coinControl)) {\n\n                        if (nValueIn < nValueToSelect) {\n                            strFailReason = _("Insufficient funds");\n                        }\n                        return false;\n                    }\n\n                    for (auto const &pcoin : setCoins) {\n                        CAmount nCredit = pcoin.first->tx->vout[pcoin.second].nValue;\n                        //The coin age after the next block (depth+1) is used instead of the current,\n                        //reflecting an assumption the user would accept a bit more delay for\n                        //a chance at a free transaction.\n                        //But mempool inputs might still be in the mempool, so their age stays 0\n                        int age = pcoin.first->GetDepthInMainChain();\n                        assert(age >= 0);\n                        if (age != 0)\n                            age += 1;\n                        dPriority += (double) nCredit * age;\n                    }\n\n                    CAmount nChange = nValueIn - nValueToSelect;\n\n                    if (nChange > 0) {\n                        // Fill a vout to ourself\n                        // TODO: pass in scriptChange instead of reservekey so\n                        // change transaction isn\'t always pay-to-bitcoin-address\n                        CScript scriptChange;\n\n                        // coin control: send change to custom address\n                        if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange))\n                            scriptChange = GetScriptForDestination(coinControl->destChange);\n\n                            // send change to one of the specified change addresses\n                        else if (IsArgSet("-change") && mapMultiArgs.at("-change").size() > 0) {\n                            CBitcoinAddress address(\n                                    mapMultiArgs.at("change")[GetRandInt(mapMultiArgs.at("-change").size())]);\n                            CKeyID keyID;\n                            if (!address.GetKeyID(keyID)) {\n                                strFailReason = _("Bad change address");\n                                return false;\n                            }\n                            scriptChange = GetScriptForDestination(keyID);\n                        }\n\n                            // no coin control: send change to newly generated address\n                        else {\n                            // Note: We use a new key here to keep it from being obvious which side is the change.\n                            //  The drawback is that by not reusing a previous key, the change may be lost if a\n                            //  backup is restored, if the backup doesn\'t have the new private key for the change.\n                            //  If we reused the old key, it would be possible to add code to look for and\n                            //  rediscover unknown transactions that were written with keys of ours to recover\n                            //  post-backup change.\n\n                            // Reserve a new key pair from key pool\n                            CPubKey vchPubKey;\n                            bool ret;\n                            ret = reservekey.GetReservedKey(vchPubKey);\n                            if (!ret) {\n                                strFailReason = _("Keypool ran out, please call keypoolrefill first");\n                                return false;\n                            }\n\n                            scriptChange = GetScriptForDestination(vchPubKey.GetID());\n                        }\n\n                        CTxOut newTxOut(nChange, scriptChange);\n\n                        // Never create dust outputs; if we would, just\n                        // add the dust to the fee.\n                        if (newTxOut.IsDust(::minRelayTxFee)) {\n                            nChangePosInOut = -1;\n                            nFeeRet += nChange;\n                            reservekey.ReturnKey();\n                        } else {\n\n                            if (nChangePosInOut == -1) {\n\n                                // Insert change txn at random position:\n                                nChangePosInOut = GetRandInt(tx.vout.size() + 1);\n                            } else if ((unsigned int) nChangePosInOut > tx.vout.size()) {\n\n                                strFailReason = _("Change index out of range");\n                                return false;\n                            }\n\n                            std::vector<CTxOut>::iterator position = tx.vout.begin() + nChangePosInOut;\n                            tx.vout.insert(position, newTxOut);\n                            wtx.changes.insert(static_cast<uint32_t>(nChangePosInOut));\n                        }\n                    } else {\n                        reservekey.ReturnKey();\n                    }\n\n                    // Fill vin\n                    //\n                    // Note how the sequence number is set to max()-1 so that the\n                    // nLockTime set above actually works.\n                    for (const auto &coin : setCoins) {\n                        tx.vin.push_back(CTxIn(\n                                coin.first->GetHash(),\n                                coin.second,\n                                CScript(),\n                                std::numeric_limits<unsigned int>::max() - 1));\n                    }\n\n                    // Fill in dummy signatures for fee calculation.\n                    if (!DummySignTx(tx, setCoins)) {\n                        strFailReason = _("Signing transaction failed");\n                        return false;\n                    }\n\n                    unsigned int nBytes = GetVirtualTransactionSize(tx);\n\n                    // Limit size\n                    CTransaction txConst(tx);\n                    if (GetTransactionWeight(txConst) >= MAX_STANDARD_TX_WEIGHT) {\n                        strFailReason = _("Transaction too large");\n                        return false;\n                    }\n                    dPriority = txConst.ComputePriority(dPriority, nBytes);\n\n                    // Remove scriptSigs to eliminate the fee calculation dummy signatures\n                    for (auto &vin : tx.vin) {\n                        vin.scriptSig = CScript();\n                        vin.scriptWitness.SetNull();\n                    }\n\n                    // Can we complete this as a free transaction?\n                    if (fSendFreeTransactions && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE) {\n                        // Not enough fee: enough priority?\n                        double dPriorityNeeded = mempool.estimateSmartPriority(nTxConfirmTarget);\n                        // Require at least hard-coded AllowFree.\n                        if (dPriority >= dPriorityNeeded && AllowFree(dPriority))\n                            break;\n                    }\n                    CAmount nFeeNeeded = GetMinimumFee(nBytes, nTxConfirmTarget, mempool);\n\n                    if (coinControl && nFeeNeeded > 0 && coinControl->nMinimumTotalFee > nFeeNeeded) {\n                        nFeeNeeded = coinControl->nMinimumTotalFee;\n                    }\n\n                    if (coinControl && coinControl->fOverrideFeeRate)\n                        nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);\n\n                    // If we made it here and we aren\'t even able to meet the relay fee on the next pass, give up\n                    // because we must be at the maximum allowed fee.\n                    if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes)) {\n                        strFailReason = _("Transaction too large for fee policy");\n                        return false;\n                    }\n\n                    if (nFeeRet >= nFeeNeeded) {\n                        for (auto &usedCoin : setCoins) {\n                            for (auto coin = itr->second.begin(); coin != itr->second.end(); coin++) {\n                                if (usedCoin.first == coin->tx && usedCoin.second == coin->i) {\n                                    itr->first -= coin->tx->tx->vout[coin->i].nValue;\n                                    itr->second.erase(coin);\n                                    break;\n                                }\n                            }\n                        }\n\n                        if (itr->second.empty()) {\n                            valueAndUTXO.erase(itr);\n                        }\n\n                        // Generate hdMint\n                        recipient = CWallet::CreateLelantusMintRecipient(privCoin, dMint);\n\n                        // vout to mint\n                        txout = CTxOut(recipient.nAmount, recipient.scriptPubKey);\n                        LogPrintf("txout: %s\\n", txout.ToString());\n\n                        for (size_t i = 0; i != tx.vout.size(); i++) {\n                            if (tx.vout[i].scriptPubKey.IsLelantusMint()) {\n                                tx.vout[i] = txout;\n                            }\n                        }\n\n                        break; // Done, enough fee included.\n                    }\n\n                    // Include more fee and try again.\n                    nFeeRet = nFeeNeeded;\n                    continue;\n                }\n\n                if(skipCoin)\n                    continue;\n\n                if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {\n                    // Lastly, ensure this tx will pass the mempool\'s chain limits\n                    LockPoints lp;\n                    CTxMemPoolEntry entry(MakeTransactionRef(tx), 0, 0, 0, 0, false, 0, lp);\n                    CTxMemPool::setEntries setAncestors;\n                    size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);\n                    size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;\n                    size_t nLimitDescendants = GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);\n                    size_t nLimitDescendantSize =\n                            GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;\n                    std::string errString;\n                    if (!mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize,\n                                                           nLimitDescendants, nLimitDescendantSize, errString)) {\n                        strFailReason = _("Transaction has too long of a mempool chain");\n                        return false;\n                    }\n                }\n\n                // Sign\n                int nIn = 0;\n                CTransaction txNewConst(tx);\n                for (const auto &coin : setCoins) {\n                    bool signSuccess = false;\n                    const CScript &scriptPubKey = coin.first->tx->vout[coin.second].scriptPubKey;\n                    SignatureData sigdata;\n                    if (sign)\n                        signSuccess = ProduceSignature(TransactionSignatureCreator(this, &txNewConst, nIn,\n                                                                                   coin.first->tx->vout[coin.second].nValue,\n                                                                                   SIGHASH_ALL), scriptPubKey,\n                                                       sigdata);\n                    else\n                        signSuccess = ProduceSignature(DummySignatureCreator(this), scriptPubKey, sigdata);\n\n                    if (!signSuccess) {\n                        strFailReason = _("Signing transaction failed");\n                        return false;\n                    } else {\n                        UpdateTransaction(tx, nIn, sigdata);\n                    }\n                    nIn++;\n                }\n\n                wtx.SetTx(MakeTransactionRef(std::move(tx)));\n\n                wtxAndFee.push_back(std::make_pair(wtx, nFeeRet));\n\n                if (nChangePosInOut >= 0) {\n                    // Cache wtx to somewhere because COutput use pointer of it.\n                    cacheWtxs.push_back(wtx);\n                    auto &wtx = cacheWtxs.back();\n\n                    COutput out(&wtx, nChangePosInOut, wtx.GetDepthInMainChain(false), true, true);\n                    auto val = wtx.tx->vout[nChangePosInOut].nValue;\n\n                    bool added = false;\n                    for (auto &utxos : valueAndUTXO) {\n                        auto const &o = utxos.second.front();\n                        if (o.tx->tx->vout[o.i].scriptPubKey == wtx.tx->vout[nChangePosInOut].scriptPubKey) {\n                            utxos.first += val;\n                            utxos.second.push_back(out);\n\n                            added = true;\n                        }\n                    }\n\n                    if (!added) {\n                        valueAndUTXO.push_back({val, {out}});\n                    }\n                }\n\n                nAllFeeRet += nFeeRet;\n                dMints.push_back(dMint);\n                if(!autoMintAll) {\n                    valueToMint -= mintedValue;\n                    if (valueToMint == 0)\n                        break;\n                }\n            }\n        }\n    }\n\n    if (!autoMintAll && valueToMint > 0) {\n        return false;\n    }\n\n    return true;\n}\n\nbool\nCWallet::CreateMintTransaction(CScript pubCoin, int64_t nValue, CWalletTx &wtxNew, CReserveKey &reservekey,\n                                       int64_t &nFeeRet, std::string &strFailReason,\n                                       const CCoinControl *coinControl) {\n    std::vector <CRecipient> vecSend;\n    CRecipient recipient = {pubCoin, nValue, false};\n    vecSend.push_back(recipient);\n    int nChangePosRet = -1;\n    return CreateMintTransaction(vecSend, wtxNew, reservekey, nFeeRet, nChangePosRet, strFailReason,\n                                         coinControl);\n}\n\nCWalletTx CWallet::CreateSigmaSpendTransaction(\n    const std::vector<CRecipient>& recipients,\n    CAmount& fee,\n    std::vector<CSigmaEntry>& selected,\n    std::vector<CHDMint>& changes,\n    bool& fChangeAddedToFee,\n    const CCoinControl *coinControl)\n{\n    // sanity check\n    EnsureMintWalletAvailable();\n\n    if (IsLocked()) {\n        throw std::runtime_error(_("Wallet locked"));\n    }\n\n    // create transaction\n    SigmaSpendBuilder builder(*this, *zwallet, coinControl);\n    CWalletDB walletdb(strWalletFile);\n    CWalletTx tx = builder.Build(recipients, fee, fChangeAddedToFee, walletdb);\n    selected = builder.selected;\n    changes = builder.changes;\n\n    return tx;\n}\n\nstd::string CWallet::MintAndStoreSigma(const std::vector<CRecipient>& vecSend,\n                                       const std::vector<sigma::PrivateCoin>& privCoins,\n                                       std::vector<CHDMint> vDMints,\n                                       CWalletTx &wtxNew, bool fAskFee,\n                                       const CCoinControl *coinControl) {\n    std::string strError;\n\n    EnsureMintWalletAvailable();\n\n    if (IsLocked()) {\n        strError = _("Error: Wallet locked, unable to create transaction!");\n        LogPrintf("MintSigma() : %s", strError);\n        return strError;\n    }\n\n    int totalValue = 0;\n    BOOST_FOREACH(CRecipient recipient, vecSend){\n        // Check amount\n        if (recipient.nAmount <= 0)\n            return _("Invalid amount");\n\n        LogPrintf("MintSigma: value = %s\\n", recipient.nAmount);\n        totalValue += recipient.nAmount;\n\n    }\n\n    if ((totalValue + payTxFee.GetFeePerK()) > GetBalance())\n        return _("Insufficient funds");\n\n    LogPrintf("payTxFee.GetFeePerK()=%s\\n", payTxFee.GetFeePerK());\n    CReserveKey reservekey(this);\n    int64_t nFeeRequired = 0;\n\n    int nChangePosRet = -1;\n\n    if (!CreateMintTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError, coinControl)) {\n        LogPrintf("nFeeRequired=%s\\n", nFeeRequired);\n        if (totalValue + nFeeRequired > GetBalance())\n            return strprintf(\n                    _("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!"),\n                    FormatMoney(nFeeRequired).c_str());\n        return strError;\n    }\n\n    if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)){\n        LogPrintf("MintSigma: returning aborted..\\n");\n        return "ABORTED";\n    }\n\n    CValidationState state;\n    if (!CommitTransaction(wtxNew, reservekey, g_connman.get(), state)) {\n        return _(\n                "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");\n    } else {\n        LogPrintf("CommitTransaction success!\\n");\n    }\n\n\n    //update mints with full transaction hash and then database them\n    CWalletDB walletdb(pwalletMain->strWalletFile);\n    for (CHDMint dMint : vDMints) {\n        dMint.SetTxHash(wtxNew.GetHash());\n        zwallet->GetTracker().Add(walletdb, dMint, true);\n        NotifyZerocoinChanged(this,\n             dMint.GetPubcoinValue().GetHex(),\n            "New (" + std::to_string(dMint.GetAmount()) + " mint)",\n            CT_NEW);\n    }\n\n    // Update nCountNextUse in HDMint wallet database\n    zwallet->UpdateCountDB(walletdb);\n\n    return "";\n}\n\nstd::string CWallet::MintAndStoreLelantus(const CAmount& value,\n                                          std::vector<std::pair<CWalletTx, CAmount>>& wtxAndFee,\n                                          std::vector<CHDMint>& mints,\n                                          bool autoMintAll,\n                                          bool fAskFee,\n                                          const CCoinControl *coinControl) {\n    std::string strError;\n\n    EnsureMintWalletAvailable();\n\n    if (IsLocked()) {\n        strError = _("Error: Wallet locked, unable to create transaction!");\n        LogPrintf("MintLelantus() : %s", strError);\n        return strError;\n    }\n\n\n    if ((value + payTxFee.GetFeePerK()) > GetBalance())\n        return _("Insufficient funds");\n\n    LogPrintf("payTxFee.GetFeePerK()=%s\\n", payTxFee.GetFeePerK());\n    int64_t nFeeRequired = 0;\n\n    int nChangePosRet = -1;\n\n    std::vector<CHDMint> dMints;\n    std::list<CReserveKey> reservekeys;\n    if (!CreateLelantusMintTransactions(value, wtxAndFee, nFeeRequired, dMints, reservekeys, nChangePosRet, strError, coinControl, autoMintAll)) {\n        return strError;\n    }\n\n    if (fAskFee && !uiInterface.ThreadSafeAskFee(nFeeRequired)){\n        LogPrintf("MintLelantus: returning aborted..\\n");\n        return "ABORTED";\n    }\n\n    CValidationState state;\n    CWalletDB walletdb(pwalletMain->strWalletFile);\n\n    auto reservekey = reservekeys.begin();\n    for(size_t i = 0; i < wtxAndFee.size(); i++) {\n        if (!CommitTransaction(wtxAndFee[i].first, *reservekey++, g_connman.get(), state)) {\n            return _(\n                    "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");\n        } else {\n            LogPrintf("CommitTransaction success!\\n");\n        }\n\n        //update mints with full transaction hash and then database them\n        CHDMint dMintTmp = dMints[i];\n        mints.push_back(dMints[i]);\n        dMintTmp.SetTxHash(wtxAndFee[i].first.GetHash());\n        zwallet->GetTracker().AddLelantus(walletdb, dMintTmp, true);\n        NotifyZerocoinChanged(this,\n            dMintTmp.GetPubcoinValue().GetHex(),\n            "New (" + std::to_string(dMintTmp.GetAmount()) + " mint)",\n            CT_NEW);\n    }\n    // Update nCountNextUse in HDMint wallet database\n    zwallet->UpdateCountDB(walletdb);\n\n    return "";\n}\n\nstd::vector<CSigmaEntry> CWallet::SpendSigma(const std::vector<CRecipient>& recipients, CWalletTx& result)\n{\n    CAmount fee;\n\n    return SpendSigma(recipients, result, fee);\n}\n\nstd::vector<CSigmaEntry> CWallet::SpendSigma(\n    const std::vector<CRecipient>& recipients,\n    CWalletTx& result,\n    CAmount& fee)\n{\n    // create transaction\n    std::vector<CSigmaEntry> coins;\n    std::vector<CHDMint> changes;\n    bool fChangeAddedToFee;\n    result = CreateSigmaSpendTransaction(recipients, fee, coins, changes, fChangeAddedToFee);\n\n    CommitSigmaTransaction(result, coins, changes);\n\n    return coins;\n}\n\nbool CWallet::CommitSigmaTransaction(CWalletTx& wtxNew, std::vector<CSigmaEntry>& selectedCoins, std::vector<CHDMint>& changes) {\n    EnsureMintWalletAvailable();\n\n    // commit\n    try {\n        CValidationState state;\n        CReserveKey reserveKey(this);\n        CommitTransaction(wtxNew, reserveKey, g_connman.get(), state);\n    } catch (...) {\n        auto error = _(\n            "Error: The transaction was rejected! This might happen if some of "\n            "the coins in your wallet were already spent, such as if you used "\n            "a copy of wallet.dat and coins were spent in the copy but not "\n            "marked as spent here."\n        );\n\n        std::throw_with_nested(std::runtime_error(error));\n    }\n\n    // mark selected coins as used\n    sigma::CSigmaState* sigmaState = sigma::CSigmaState::GetState();\n    CWalletDB db(strWalletFile);\n\n    for (auto& coin : selectedCoins) {\n        // get coin id & height\n        int height, id;\n\n        std::tie(height, id) = sigmaState->GetMintedCoinHeightAndId(sigma::PublicCoin(\n            coin.value, coin.get_denomination()));\n\n        // add CSigmaSpendEntry\n        CSigmaSpendEntry spend;\n\n        spend.coinSerial = coin.serialNumber;\n        spend.hashTx = wtxNew.GetHash();\n        spend.pubCoin = coin.value;\n        spend.id = id;\n        spend.set_denomination_value(coin.get_denomination_value());\n\n        if (!db.WriteCoinSpendSerialEntry(spend)) {\n            throw std::runtime_error(_("Failed to write coin serial number into wallet"));\n        }\n\n        //Set spent mint as used in memory\n        uint256 hashPubcoin = primitives::GetPubCoinValueHash(coin.value);\n        zwallet->GetTracker().SetPubcoinUsed(hashPubcoin, wtxNew.GetHash());\n        CMintMeta metaCheck;\n        zwallet->GetTracker().GetMetaFromPubcoin(hashPubcoin, metaCheck);\n        if (!metaCheck.isUsed) {\n            std::string strError = "Error, mint with pubcoin hash " + hashPubcoin.GetHex() + " did not get marked as used";\n            LogPrintf("SpendSigma() : %s\\n", strError.c_str());\n        }\n\n        //Set spent mint as used in DB\n        zwallet->GetTracker().UpdateState(metaCheck);\n\n        // update CSigmaEntry\n        coin.IsUsed = true;\n        coin.id = id;\n        coin.nHeight = height;\n\n        // raise event\n        NotifyZerocoinChanged(\n            this,\n            coin.value.GetHex(),\n            "Used (" + std::to_string(coin.get_denomination()) + " mint)",\n            CT_UPDATED);\n    }\n\n    for (auto& change : changes) {\n        change.SetTxHash(wtxNew.GetHash());\n        zwallet->GetTracker().Add(db, change, true);\n\n        // raise event\n        NotifyZerocoinChanged(this,\n            change.GetPubcoinValue().GetHex(),\n            "New (" + std::to_string(change.GetAmount()) + " mint)",\n            CT_NEW);\n    }\n\n    // Update nCountNextUse in HDMint wallet database\n    zwallet->UpdateCountDB(db);\n\n    return true;\n}\n\nstd::vector<CLelantusEntry> CWallet::JoinSplitLelantus(const std::vector<CRecipient>& recipients, const std::vector<CAmount>& newMints, CWalletTx& result) {\n    // create transaction\n    std::vector<CLelantusEntry> spendCoins; //spends\n    std::vector<CSigmaEntry> sigmaSpendCoins;\n    std::vector<CHDMint> mintCoins; // new mints\n    CAmount fee;\n    result = CreateLelantusJoinSplitTransaction(recipients, fee, newMints, spendCoins, sigmaSpendCoins, mintCoins);\n\n    CommitLelantusTransaction(result, spendCoins, sigmaSpendCoins, mintCoins);\n\n    return spendCoins;\n}\n\nCWalletTx CWallet::CreateLelantusJoinSplitTransaction(\n        const std::vector<CRecipient>& recipients,\n        CAmount &fee,\n        const std::vector<CAmount>& newMints,\n        std::vector<CLelantusEntry>& spendCoins,\n        std::vector<CSigmaEntry>& sigmaSpendCoins,\n        std::vector<CHDMint>& mintCoins,\n        const CCoinControl *coinControl,\n        std::function<void(CTxOut & , LelantusJoinSplitBuilder const &)> modifier)\n{\n    // sanity check\n    EnsureMintWalletAvailable();\n\n    if (IsLocked()) {\n        throw std::runtime_error(_("Wallet locked"));\n    }\n\n    // create transaction\n    LelantusJoinSplitBuilder builder(*this, *zwallet, coinControl);\n\n    CWalletTx tx = builder.Build(recipients, fee, newMints, modifier);\n    spendCoins = builder.spendCoins;\n    sigmaSpendCoins = builder.sigmaSpendCoins;\n    mintCoins = builder.mintCoins;\n\n    return tx;\n}\n\nstd::pair<CAmount, unsigned int> CWallet::EstimateJoinSplitFee(\n        CAmount required,\n        bool subtractFeeFromAmount,\n        std::list<CSigmaEntry> sigmaCoins,\n        std::list<CLelantusEntry> coins,\n        const CCoinControl *coinControl) {\n    CAmount fee;\n    unsigned size;\n    std::vector<CLelantusEntry> spendCoins;\n    std::vector<CSigmaEntry> sigmaSpendCoins;\n\n    CAmount availableSigmaBalance(0);\n    for (auto coin : sigmaCoins) {\n        availableSigmaBalance += coin.get_denomination_value();\n    }\n\n    for (fee = payTxFee.GetFeePerK();;) {\n        CAmount currentRequired = required;\n\n        if (!subtractFeeFromAmount)\n            currentRequired += fee;\n\n        spendCoins.clear();\n        sigmaSpendCoins.clear();\n        const auto &consensusParams = Params().GetConsensus();\n        CAmount changeToMint = 0;\n\n        std::vector<sigma::CoinDenomination> denomChanges;\n        try {\n            if (availableSigmaBalance > 0) {\n                CAmount inputFromSigma;\n                if (currentRequired > availableSigmaBalance)\n                    inputFromSigma = availableSigmaBalance;\n                else\n                    inputFromSigma = currentRequired;\n                this->GetCoinsToSpend(inputFromSigma, sigmaSpendCoins, denomChanges, sigmaCoins, //try to spend sigma first\n                                       consensusParams.nMaxLelantusInputPerTransaction,\n                                       consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl);\n                currentRequired -= inputFromSigma;\n            }\n\n            if (currentRequired > 0) {\n                if (!this->GetCoinsToJoinSplit(currentRequired, spendCoins, changeToMint, coins,\n                                                consensusParams.nMaxLelantusInputPerTransaction,\n                                                consensusParams.nMaxValueLelantusSpendPerTransaction, coinControl)) {\n                    return std::make_pair(0, 0);\n                }\n            }\n        } catch (std::runtime_error const &) {\n        }\n\n        // 1054 is constant part, mainly Schnorr and Range proofs, 2560 is for each sigma/aux data\n        // 179 other parts of tx, assuming 1 utxo and 1 jmint\n        size = 1054 + 2560 * (spendCoins.size() + sigmaSpendCoins.size()) + 179;\n        CAmount feeNeeded = CWallet::GetMinimumFee(size, nTxConfirmTarget, mempool);\n\n        if (fee >= feeNeeded) {\n            break;\n        }\n\n        fee = feeNeeded;\n\n        if(subtractFeeFromAmount)\n            break;\n    }\n\n    return std::make_pair(fee, size);\n}\n\nbool CWallet::CommitLelantusTransaction(CWalletTx& wtxNew, std::vector<CLelantusEntry>& spendCoins, std::vector<CSigmaEntry>& sigmaSpendCoins, std::vector<CHDMint>& mintCoins) {\n    EnsureMintWalletAvailable();\n\n    // commit\n    try {\n        CValidationState state;\n        CReserveKey reserveKey(this);\n        CommitTransaction(wtxNew, reserveKey, g_connman.get(), state);\n    } catch (...) {\n        auto error = _(\n                "Error: The transaction was rejected! This might happen if some of "\n                "the coins in your wallet were already spent, such as if you used "\n                "a copy of wallet.dat and coins were spent in the copy but not "\n                "marked as spent here."\n        );\n\n        std::throw_with_nested(std::runtime_error(error));\n    }\n\n    // mark selected coins as used\n    lelantus::CLelantusState* lelantusState = lelantus::CLelantusState::GetState();\n    CWalletDB db(strWalletFile);\n\n    for (auto& coin : spendCoins) {\n        // get coin id & height\n        int height, id;\n\n        std::tie(height, id) = lelantusState->GetMintedCoinHeightAndId(lelantus::PublicCoin(coin.value));\n\n        // add CLelantusSpendEntry\n        CLelantusSpendEntry spend;\n\n        spend.coinSerial = coin.serialNumber;\n        spend.hashTx = wtxNew.GetHash();\n        spend.pubCoin = coin.value;\n        spend.id = id;\n        spend.amount = coin.amount;\n\n        if (!db.WriteLelantusSpendSerialEntry(spend)) {\n            throw std::runtime_error(_("Failed to write coin serial number into wallet"));\n        }\n\n        //Set spent mint as used in memory\n        uint256 hashPubcoin = primitives::GetPubCoinValueHash(coin.value);\n        zwallet->GetTracker().SetLelantusPubcoinUsed(hashPubcoin, wtxNew.GetHash());\n        CLelantusMintMeta metaCheck;\n        zwallet->GetTracker().GetLelantusMetaFromPubcoin(hashPubcoin, metaCheck);\n        if (!metaCheck.isUsed) {\n            std::string strError = "Error, mint with pubcoin hash " + hashPubcoin.GetHex() + " did not get marked as used";\n            LogPrintf("SpendLelantus() : %s\\n", strError.c_str());\n        }\n\n        //Set spent mint as used in DB\n        zwallet->GetTracker().UpdateState(metaCheck);\n\n        // update CLelantusEntry\n        coin.IsUsed = true;\n        coin.id = id;\n        coin.nHeight = height;\n\n        // raise event\n        NotifyZerocoinChanged(\n                this,\n                coin.value.GetHex(),\n                "Used (" + std::to_string(coin.amount) + " mint)",\n                CT_UPDATED);\n    }\n\n    sigma::CSigmaState* sigmaState = sigma::CSigmaState::GetState();\n    for (auto& coin : sigmaSpendCoins) {\n        // get coin id & height\n        int height, id;\n\n        std::tie(height, id) = sigmaState->GetMintedCoinHeightAndId(sigma::PublicCoin(\n            coin.value, coin.get_denomination()));\n\n        // add CSigmaSpendEntry\n        CSigmaSpendEntry spend;\n\n        spend.coinSerial = coin.serialNumber;\n        spend.hashTx = wtxNew.GetHash();\n        spend.pubCoin = coin.value;\n        spend.id = id;\n        spend.set_denomination_value(coin.get_denomination_value());\n\n        if (!db.WriteCoinSpendSerialEntry(spend)) {\n            throw std::runtime_error(_("Failed to write coin serial number into wallet"));\n        }\n\n        //Set spent mint as used in memory\n        uint256 hashPubcoin = primitives::GetPubCoinValueHash(coin.value);\n        zwallet->GetTracker().SetPubcoinUsed(hashPubcoin, wtxNew.GetHash());\n        CMintMeta metaCheck;\n        zwallet->GetTracker().GetMetaFromPubcoin(hashPubcoin, metaCheck);\n        if (!metaCheck.isUsed) {\n            std::string strError = "Error, mint with pubcoin hash " + hashPubcoin.GetHex() + " did not get marked as used";\n            LogPrintf("SpendZerocoin() : %s\\n", strError.c_str());\n        }\n\n        //Set spent mint as used in DB\n        zwallet->GetTracker().UpdateState(metaCheck);\n\n        // update CSigmaEntry\n        coin.IsUsed = true;\n        coin.id = id;\n        coin.nHeight = height;\n\n        // raise event\n        NotifyZerocoinChanged(\n            this,\n            coin.value.GetHex(),\n            "Used (" + std::to_string(coin.get_denomination()) + " mint)",\n            CT_UPDATED);\n    }\n\n    for (auto& coin : mintCoins) {\n        coin.SetTxHash(wtxNew.GetHash());\n        zwallet->GetTracker().AddLelantus(db, coin, true);\n\n        // raise event\n        NotifyZerocoinChanged(this,\n                              coin.GetPubcoinValue().GetHex(),\n                              "New (" + std::to_string(coin.GetAmount()) + " mint)",\n                              CT_NEW);\n    }\n\n    // Update nCountNextUse in HDMint wallet database\n    zwallet->UpdateCountDB(db);\n\n    return true;\n}\n\n\nbool CWallet::GetMint(const uint256& hashSerial, CSigmaEntry& sigmaEntry, bool forEstimation) const\n{\n    EnsureMintWalletAvailable();\n\n    if (IsLocked() && !forEstimation) {\n        return false;\n    }\n\n    CMintMeta meta;\n    if(!zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta))\n        return error("%s: serialhash %s is not in tracker", __func__, hashSerial.GetHex());\n\n    CWalletDB walletdb(strWalletFile);\n     if (meta.isDeterministic) {\n        CHDMint dMint;\n        if (!walletdb.ReadHDMint(meta.GetPubCoinValueHash(), false, dMint))\n            return error("%s: failed to read deterministic mint", __func__);\n        if (!zwallet->RegenerateMint(walletdb, dMint, sigmaEntry, forEstimation))\n            return error("%s: failed to generate mint", __func__);\n\n         return true;\n    } else if (!walletdb.ReadSigmaEntry(meta.GetPubCoinValue(), sigmaEntry)) {\n        return error("%s: failed to read sigmamint from database", __func__);\n    }\n\n     return true;\n}\n\nbool CWallet::GetMint(const uint256& hashSerial, CLelantusEntry& mint, bool forEstimation) const\n{\n    EnsureMintWalletAvailable();\n\n    if (IsLocked() && !forEstimation) {\n        return false;\n    }\n\n    CLelantusMintMeta meta;\n    if(!zwallet->GetTracker().GetMetaFromSerial(hashSerial, meta))\n        return error("%s: serialhash %s is not in tracker", __func__, hashSerial.GetHex());\n\n    CWalletDB walletdb(strWalletFile);\n\n    CHDMint dMint;\n    if (!walletdb.ReadHDMint(meta.GetPubCoinValueHash(), true, dMint))\n        return error("%s: failed to read deterministic Lelantus mint", __func__);\n    if (!zwallet->RegenerateMint(walletdb, dMint, mint, forEstimation))\n        return error("%s: failed to generate Lelantus mint", __func__);\n    return true;\n\n}\n\nvoid CWallet::ListAccountCreditDebit(const std::string& strAccount, std::list<CAccountingEntry>& entries) {\n    CWalletDB walletdb(strWalletFile);\n    return walletdb.ListAccountCreditDebit(strAccount, entries);\n}\n\nbool CWallet::AddAccountingEntry(const CAccountingEntry& acentry)\n{\n    CWalletDB walletdb(strWalletFile);\n\n    return AddAccountingEntry(acentry, &walletdb);\n}\n\nbool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwalletdb)\n{\n    if (!pwalletdb->WriteAccountingEntry_Backend(acentry))\n        return false;\n\n    laccentries.push_back(acentry);\n    CAccountingEntry & entry = laccentries.back();\n    wtxOrdered.insert(make_pair(entry.nOrderPos, TxPair((CWalletTx*)0, &entry)));\n\n    return true;\n}\n\nCAmount CWallet::GetRequiredFee(unsigned int nTxBytes)\n{\n    return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));\n}\n\nCAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)\n{\n    // payTxFee is the user-set global for desired feerate\n    return GetMinimumFee(nTxBytes, nConfirmTarget, pool, payTxFee.GetFee(nTxBytes));\n}\n\nCAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool, CAmount targetFee)\n{\n    CAmount nFeeNeeded = targetFee;\n    // User didn\'t set: use -txconfirmtarget to estimate...\n    if (nFeeNeeded == 0) {\n        int estimateFoundTarget = nConfirmTarget;\n        nFeeNeeded = pool.estimateSmartFee(nConfirmTarget, &estimateFoundTarget).GetFee(nTxBytes);\n        // ... unless we don\'t have enough mempool data for estimatefee, then use fallbackFee\n        if (nFeeNeeded == 0)\n            nFeeNeeded = fallbackFee.GetFee(nTxBytes);\n    }\n    // prevent user from paying a fee below minRelayTxFee or minTxFee\n    nFeeNeeded = std::max(nFeeNeeded, GetRequiredFee(nTxBytes));\n    // But always obey the maximum\n    if (nFeeNeeded > maxTxFee)\n        nFeeNeeded = maxTxFee;\n\n    return nFeeNeeded;\n}\n\nDBErrors CWallet::LoadWallet(bool& fFirstRunRet)\n{\n    if (!fFileBacked)\n        return DB_LOAD_OK;\n    fFirstRunRet = false;\n    DBErrors nLoadWalletRet = CWalletDB(strWalletFile,"cr+").LoadWallet(this);\n    if (nLoadWalletRet == DB_NEED_REWRITE)\n    {\n        if (CDB::Rewrite(strWalletFile, "\\x04pool"))\n        {\n            LOCK(cs_wallet);\n            setKeyPool.clear();\n            m_pool_key_to_index.clear();\n            // Note: can\'t top-up keypool here, because wallet is locked.\n            // User will be prompted to unlock wallet the next operation\n            // that requires a new key.\n        }\n    }\n\n    {\n        LOCK2(cs_main, cs_wallet);\n        for (auto& pair : mapWallet) {\n            for(unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {\n                if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {\n                    setWalletUTXO.insert(COutPoint(pair.first, i));\n                }\n            }\n        }\n    }\n\n    if (nLoadWalletRet != DB_LOAD_OK)\n        return nLoadWalletRet;\n    fFirstRunRet = !vchDefaultKey.IsValid();\n\n    uiInterface.LoadWallet(this);\n\n    return DB_LOAD_OK;\n}\n\n// Goes through all wallet transactions and checks if they are masternode collaterals, in which case these are locked\n// This avoids accidential spending of collaterals. They can still be unlocked manually if a spend is really intended.\nvoid CWallet::AutoLockMasternodeCollaterals()\n{\n    auto mnList = deterministicMNManager->GetListAtChainTip();\n\n    LOCK2(cs_main, cs_wallet);\n    for (const auto& pair : mapWallet) {\n        for (unsigned int i = 0; i < pair.second.tx->vout.size(); ++i) {\n            if (IsMine(pair.second.tx->vout[i]) && !IsSpent(pair.first, i)) {\n                if (deterministicMNManager->IsProTxWithCollateral(pair.second.tx, i) || mnList.HasMNByCollateral(COutPoint(pair.first, i))) {\n                    LockCoin(COutPoint(pair.first, i));\n                }\n            }\n        }\n    }\n}\n\nDBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut)\n{\n    if (!fFileBacked)\n        return DB_LOAD_OK;\n    DBErrors nZapSelectTxRet = CWalletDB(strWalletFile,"cr+").ZapSelectTx(this, vHashIn, vHashOut);\n    if (nZapSelectTxRet == DB_NEED_REWRITE)\n    {\n        if (CDB::Rewrite(strWalletFile, "\\x04pool"))\n        {\n            LOCK(cs_wallet);\n            setKeyPool.clear();\n            m_pool_key_to_index.clear();\n            // Note: can\'t top-up keypool here, because wallet is locked.\n            // User will be prompted to unlock wallet the next operation\n            // that requires a new key.\n        }\n    }\n\n    if (nZapSelectTxRet != DB_LOAD_OK)\n        return nZapSelectTxRet;\n\n    MarkDirty();\n\n    return DB_LOAD_OK;\n\n}\n\nDBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx)\n{\n    if (!fFileBacked)\n        return DB_LOAD_OK;\n    DBErrors nZapWalletTxRet = CWalletDB(strWalletFile,"cr+").ZapWalletTx(this, vWtx);\n    if (nZapWalletTxRet == DB_NEED_REWRITE)\n    {\n        if (CDB::Rewrite(strWalletFile, "\\x04pool"))\n        {\n            LOCK(cs_wallet);\n            setKeyPool.clear();\n            m_pool_key_to_index.clear();\n            // Note: can\'t top-up keypool here, because wallet is locked.\n            // User will be prompted to unlock wallet the next operation\n            // that requires a new key.\n        }\n    }\n\n    if (nZapWalletTxRet != DB_LOAD_OK)\n        return nZapWalletTxRet;\n\n    return DB_LOAD_OK;\n}\n\nDBErrors CWallet::ZapSigmaMints() {\n    if (!fFileBacked)\n        return DB_LOAD_OK;\n    DBErrors nZapSigmaMintRet = CWalletDB(strWalletFile, "cr+").ZapSigmaMints(this);\n    if (nZapSigmaMintRet != DB_LOAD_OK){\n        LogPrintf("Failed to remmove Sigma mints from CWalletDB");\n        return nZapSigmaMintRet;\n    }\n\n    return DB_LOAD_OK;\n}\n\nDBErrors CWallet::ZapLelantusMints() {\n    if (!fFileBacked)\n        return DB_LOAD_OK;\n    DBErrors nZapLelantusMintRet = CWalletDB(strWalletFile, "cr+").ZapLelantusMints(this);\n    if (nZapLelantusMintRet != DB_LOAD_OK){\n        LogPrintf("Failed to remove Lelantus mints from CWalletDB");\n        return nZapLelantusMintRet;\n    }\n\n    return DB_LOAD_OK;\n}\n\n\nbool CWallet::SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& strPurpose)\n{\n    bool fUpdated = false;\n    {\n        LOCK(cs_wallet); // mapAddressBook\n        std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address);\n        fUpdated = mi != mapAddressBook.end();\n        mapAddressBook[address].name = strName;\n        if (!strPurpose.empty()) /* update purpose only if requested */\n            mapAddressBook[address].purpose = strPurpose;\n    }\n    NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address) != ISMINE_NO,\n                             strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );\n    if (!fFileBacked)\n        return false;\n    if (!strPurpose.empty() && !CWalletDB(strWalletFile).WritePurpose(CBitcoinAddress(address).ToString(), strPurpose))\n        return false;\n    return CWalletDB(strWalletFile).WriteName(CBitcoinAddress(address).ToString(), strName);\n}\n\nbool CWallet::DelAddressBook(const CTxDestination& address)\n{\n    {\n        LOCK(cs_wallet); // mapAddressBook\n\n        if(fFileBacked)\n        {\n            // Delete destdata tuples associated with address\n            std::string strAddress = CBitcoinAddress(address).ToString();\n            BOOST_FOREACH(const PAIRTYPE(std::string, std::string) &item, mapAddressBook[address].destdata)\n            {\n                CWalletDB(strWalletFile).EraseDestData(strAddress, item.first);\n            }\n        }\n        mapAddressBook.erase(address);\n    }\n\n    NotifyAddressBookChanged(this, address, "", ::IsMine(*this, address) != ISMINE_NO, "", CT_DELETED);\n\n    if (!fFileBacked)\n        return false;\n    CWalletDB(strWalletFile).ErasePurpose(CBitcoinAddress(address).ToString());\n    return CWalletDB(strWalletFile).EraseName(CBitcoinAddress(address).ToString());\n}\n\nconst std::string& CWallet::GetAccountName(const CScript& scriptPubKey) const\n{\n    CTxDestination address;\n    if (ExtractDestination(scriptPubKey, address) && !scriptPubKey.IsUnspendable()) {\n        auto mi = mapAddressBook.find(address);\n        if (mi != mapAddressBook.end()) {\n            return mi->second.name;\n        }\n    }\n    // A scriptPubKey that doesn\'t have an entry in the address book is\n    // associated with the default account ("").\n    const static std::string DEFAULT_ACCOUNT_NAME;\n    return DEFAULT_ACCOUNT_NAME;\n}\n\nbool CWallet::SetDefaultKey(const CPubKey &vchPubKey)\n{\n    if (fFileBacked)\n    {\n        if (!CWalletDB(strWalletFile).WriteDefaultKey(vchPubKey))\n            return false;\n    }\n    vchDefaultKey = vchPubKey;\n    return true;\n}\n\n/**\n * Mark old keypool keys as used,\n * and generate all new keys\n */\nbool CWallet::NewKeyPool()\n{\n    {\n        LOCK(cs_wallet);\n        CWalletDB walletdb(strWalletFile);\n        BOOST_FOREACH(int64_t nIndex, setKeyPool)\n            walletdb.ErasePool(nIndex);\n        setKeyPool.clear();\n        m_pool_key_to_index.clear();\n\n        if (IsLocked())\n            return false;\n\n        int64_t nKeys = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t)0);\n        for (int i = 0; i < nKeys; i++)\n        {\n            int64_t nIndex = i+1;\n            CPubKey pubkey(GenerateNewKey());\n            walletdb.WritePool(nIndex, CKeyPool(pubkey));\n            setKeyPool.insert(nIndex);\n            m_pool_key_to_index[pubkey.GetID()] = nIndex;\n        }\n        LogPrintf("CWallet::NewKeyPool wrote %d new keys\\n", nKeys);\n    }\n    return true;\n}\n\nbool CWallet::TopUpKeyPool(unsigned int kpSize)\n{\n    {\n        LOCK(cs_wallet);\n\n        if (IsLocked())\n            return false;\n\n        CWalletDB walletdb(strWalletFile);\n\n        // Top up key pool\n        unsigned int nTargetSize;\n        if (kpSize > 0)\n            nTargetSize = kpSize;\n        else\n            nTargetSize = std::max(GetArg("-keypool", DEFAULT_KEYPOOL_SIZE), (int64_t) 0);\n\n        while (setKeyPool.size() < (nTargetSize + 1))\n        {\n            int64_t nEnd = 1;\n            if (!setKeyPool.empty())\n                nEnd = *(--setKeyPool.end()) + 1;\n            CPubKey pubkey(GenerateNewKey());\n            if (!walletdb.WritePool(nEnd, CKeyPool(pubkey)))\n                throw std::runtime_error(std::string(__func__) + ": writing generated key failed");\n            setKeyPool.insert(nEnd);\n            LogPrintf("keypool added key %d, size=%u\\n", nEnd, setKeyPool.size());\n            m_pool_key_to_index[pubkey.GetID()] = nEnd;\n        }\n    }\n    return true;\n}\n\nvoid CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool)\n{\n    nIndex = -1;\n    keypool.vchPubKey = CPubKey();\n    {\n        LOCK(cs_wallet);\n\n        if (!IsLocked())\n            TopUpKeyPool();\n\n        // Get the oldest key\n        if(setKeyPool.empty())\n            return;\n\n        CWalletDB walletdb(strWalletFile);\n\n        nIndex = *(setKeyPool.begin());\n        setKeyPool.erase(setKeyPool.begin());\n        if (!walletdb.ReadPool(nIndex, keypool))\n            throw std::runtime_error(std::string(__func__) + ": read failed");\n        if (!HaveKey(keypool.vchPubKey.GetID()))\n            throw std::runtime_error(std::string(__func__) + ": unknown key in key pool");\n\n        assert(keypool.vchPubKey.IsValid());\n        m_pool_key_to_index.erase(keypool.vchPubKey.GetID());\n        LogPrintf("keypool reserve %d\\n", nIndex);\n    }\n}\n\nvoid CWallet::KeepKey(int64_t nIndex)\n{\n    // Remove from key pool\n    if (fFileBacked)\n    {\n        CWalletDB walletdb(strWalletFile);\n        walletdb.ErasePool(nIndex);\n    }\n    LogPrintf("keypool keep %d\\n", nIndex);\n}\n\nvoid CWallet::ReturnKey(int64_t nIndex, const CPubKey& pubkey)\n{\n    // Return to key pool\n    {\n        LOCK(cs_wallet);\n        setKeyPool.insert(nIndex);\n        m_pool_key_to_index[pubkey.GetID()] = nIndex;\n    }\n    LogPrintf("keypool return %d\\n", nIndex);\n}\n\nbool CWallet::GetKeyFromPool(CPubKey& result)\n{\n    int64_t nIndex = 0;\n    CKeyPool keypool;\n    {\n        LOCK(cs_wallet);\n        ReserveKeyFromKeyPool(nIndex, keypool);\n        if (nIndex == -1)\n        {\n            if (IsLocked()) return false;\n            result = GenerateNewKey();\n            return true;\n        }\n        KeepKey(nIndex);\n        result = keypool.vchPubKey;\n    }\n    return true;\n}\n\nint64_t CWallet::GetOldestKeyPoolTime()\n{\n    LOCK(cs_wallet);\n\n    // if the keypool is empty, return <NOW>\n    if (setKeyPool.empty())\n        return GetTime();\n\n    // load oldest key from keypool, get time and return\n    CKeyPool keypool;\n    CWalletDB walletdb(strWalletFile);\n    int64_t nIndex = *(setKeyPool.begin());\n    if (!walletdb.ReadPool(nIndex, keypool))\n        throw std::runtime_error(std::string(__func__) + ": read oldest key in keypool failed");\n    assert(keypool.vchPubKey.IsValid());\n    return keypool.nTime;\n}\n\nstd::map<CTxDestination, CAmount> CWallet::GetAddressBalances()\n{\n    std::map<CTxDestination, CAmount> balances;\n\n    {\n        LOCK(cs_wallet);\n        BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)\n        {\n            CWalletTx *pcoin = &walletEntry.second;\n\n            if (!pcoin->IsTrusted())\n                continue;\n\n            if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)\n                continue;\n\n            int nDepth = pcoin->GetDepthInMainChain();\n            if ((nDepth < (pcoin->IsFromMe(ISMINE_ALL) ? 0 : 1)) && !pcoin->IsLockedByLLMQInstantSend())\n                continue;\n\n            for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)\n            {\n                CTxDestination addr;\n                if (!IsMine(pcoin->tx->vout[i]))\n                    continue;\n                if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr))\n                    continue;\n\n                CAmount n = IsSpent(walletEntry.first, i) ? 0 : pcoin->tx->vout[i].nValue;\n\n                if (!balances.count(addr))\n                    balances[addr] = 0;\n                balances[addr] += n;\n            }\n        }\n    }\n\n    return balances;\n}\n\nstd::set< std::set<CTxDestination> > CWallet::GetAddressGroupings()\n{\n    AssertLockHeld(cs_wallet); // mapWallet\n    std::set< std::set<CTxDestination> > groupings;\n    std::set<CTxDestination> grouping;\n\n    BOOST_FOREACH(PAIRTYPE(uint256, CWalletTx) walletEntry, mapWallet)\n    {\n        CWalletTx *pcoin = &walletEntry.second;\n\n        if (pcoin->tx->vin.size() > 0 &&\n            !(pcoin->tx->HasNoRegularInputs())) { /* Spends have no standard input */\n            bool any_mine = false;\n            // group all input addresses with each other\n            BOOST_FOREACH(CTxIn txin, pcoin->tx->vin)\n            {\n                CTxDestination address;\n                if(!IsMine(txin, *pcoin->tx)) /* If this input isn\'t mine, ignore it */\n                    continue;\n                if(!ExtractDestination(mapWallet[txin.prevout.hash].tx->vout[txin.prevout.n].scriptPubKey, address))\n                    continue;\n                grouping.insert(address);\n                any_mine = true;\n            }\n\n            // group change with input addresses\n            if (any_mine)\n            {\n                for (uint32_t i = 0; i < pcoin->tx->vout.size(); i++) {\n                    if (pcoin->IsChange(i)) {\n                        CTxDestination addr;\n                        if (!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, addr)) {\n                            continue;\n                        }\n                        grouping.insert(addr);\n                    }\n                }\n            }\n            if (grouping.size() > 0)\n            {\n                groupings.insert(grouping);\n                grouping.clear();\n            }\n        }\n\n        // group lone addrs by themselves\n        for (unsigned int i = 0; i < pcoin->tx->vout.size(); i++)\n            if (IsMine(pcoin->tx->vout[i]))\n            {\n                CTxDestination address;\n                if(!ExtractDestination(pcoin->tx->vout[i].scriptPubKey, address))\n                    continue;\n                grouping.insert(address);\n                groupings.insert(grouping);\n                grouping.clear();\n            }\n    }\n\n    std::set< std::set<CTxDestination>* > uniqueGroupings; // a set of pointers to groups of addresses\n    std::map< CTxDestination, std::set<CTxDestination>* > setmap;  // map addresses to the unique group containing it\n    BOOST_FOREACH(std::set<CTxDestination> _grouping, groupings)\n    {\n        // make a set of all the groups hit by this new group\n        std::set< std::set<CTxDestination>* > hits;\n        std::map< CTxDestination, std::set<CTxDestination>* >::iterator it;\n        BOOST_FOREACH(CTxDestination address, _grouping)\n            if ((it = setmap.find(address)) != setmap.end())\n                hits.insert((*it).second);\n\n        // merge all hit groups into a new single group and delete old groups\n        std::set<CTxDestination>* merged = new std::set<CTxDestination>(_grouping);\n        BOOST_FOREACH(std::set<CTxDestination>* hit, hits)\n        {\n            merged->insert(hit->begin(), hit->end());\n            uniqueGroupings.erase(hit);\n            delete hit;\n        }\n        uniqueGroupings.insert(merged);\n\n        // update setmap\n        BOOST_FOREACH(CTxDestination element, *merged)\n            setmap[element] = merged;\n    }\n\n    std::set< std::set<CTxDestination> > ret;\n    BOOST_FOREACH(std::set<CTxDestination>* uniqueGrouping, uniqueGroupings)\n    {\n        ret.insert(*uniqueGrouping);\n        delete uniqueGrouping;\n    }\n\n    return ret;\n}\n\nstd::set<CTxDestination> CWallet::GetAccountAddresses(const std::string& strAccount) const\n{\n    LOCK(cs_wallet);\n    std::set<CTxDestination> result;\n    BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, mapAddressBook)\n    {\n        const CTxDestination& address = item.first;\n        const std::string& strName = item.second.name;\n        if (strName == strAccount)\n            result.insert(address);\n    }\n    return result;\n}\n\nbool CReserveKey::GetReservedKey(CPubKey& pubkey)\n{\n    if (nIndex == -1)\n    {\n        CKeyPool keypool;\n        pwallet->ReserveKeyFromKeyPool(nIndex, keypool);\n        if (nIndex != -1)\n            vchPubKey = keypool.vchPubKey;\n        else {\n            return false;\n        }\n    }\n    assert(vchPubKey.IsValid());\n    pubkey = vchPubKey;\n    return true;\n}\n\nvoid CReserveKey::KeepKey()\n{\n    if (nIndex != -1)\n        pwallet->KeepKey(nIndex);\n    nIndex = -1;\n    vchPubKey = CPubKey();\n}\n\nvoid CReserveKey::ReturnKey()\n{\n    if (nIndex != -1)\n        pwallet->ReturnKey(nIndex, vchPubKey);\n    nIndex = -1;\n    vchPubKey = CPubKey();\n}\n\nvoid CWallet::MarkReserveKeysAsUsed(int64_t keypool_id)\n{\n    AssertLockHeld(cs_wallet);\n    auto it = setKeyPool.begin();\n\n    CWalletDB walletdb(strWalletFile);\n    while (it != std::end(setKeyPool)) {\n        const int64_t& index = *(it);\n        if (index > keypool_id) break; // set*KeyPool is ordered\n\n        walletdb.ErasePool(index);\n        it = setKeyPool.erase(it);\n    }\n}\n\nbool CWallet::UpdatedTransaction(const uint256 &hashTx)\n{\n    {\n        LOCK(cs_wallet);\n        // Only notify UI if this transaction is in this wallet\n        std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);\n        if (mi != mapWallet.end()) {\n            NotifyTransactionChanged(this, hashTx, CT_UPDATED);\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)\n{\n    boost::shared_ptr<CReserveKey> rKey(new CReserveKey(this));\n    CPubKey pubkey;\n    if (!rKey->GetReservedKey(pubkey))\n        return;\n\n    script = rKey;\n    script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;\n}\n\nvoid CWallet::LockCoin(const COutPoint& output)\n{\n    AssertLockHeld(cs_wallet); // setLockedCoins\n    setLockedCoins.insert(output);\n}\n\nvoid CWallet::UnlockCoin(const COutPoint& output)\n{\n    AssertLockHeld(cs_wallet); // setLockedCoins\n    setLockedCoins.erase(output);\n}\n\nvoid CWallet::UnlockAllCoins()\n{\n    AssertLockHeld(cs_wallet); // setLockedCoins\n    setLockedCoins.clear();\n}\n\nbool CWallet::IsLockedCoin(uint256 hash, unsigned int n) const\n{\n    AssertLockHeld(cs_wallet); // setLockedCoins\n    COutPoint outpt(hash, n);\n\n    return (setLockedCoins.count(outpt) > 0);\n}\n\nvoid CWallet::ListLockedCoins(std::vector<COutPoint>& vOutpts)\n{\n    AssertLockHeld(cs_wallet); // setLockedCoins\n    for (std::set<COutPoint>::iterator it = setLockedCoins.begin();\n         it != setLockedCoins.end(); it++) {\n        COutPoint outpt = (*it);\n        vOutpts.push_back(outpt);\n    }\n}\n\nbool CWallet::HasMasternode(){\n\n    auto mnList = deterministicMNManager->GetListForBlock(chainActive.Tip());\n\n    AssertLockHeld(cs_wallet);\n    for (const auto &o : setWalletUTXO) {\n        if (mapWallet.count(o.hash)) {\n            const auto &p = mapWallet[o.hash];\n            if (deterministicMNManager->IsProTxWithCollateral(p.tx, o.n) || mnList.HasMNByCollateral(o)) {\n                return true;\n            }\n        }\n    }\n\n    return false;\n}\n\nvoid CWallet::ListProTxCoins(std::vector<COutPoint>& vOutpts)\n{\n    auto mnList = deterministicMNManager->GetListForBlock(chainActive.Tip());\n\n    AssertLockHeld(cs_wallet);\n    for (const auto &o : setWalletUTXO) {\n        if (mapWallet.count(o.hash)) {\n            const auto &p = mapWallet[o.hash];\n            if (deterministicMNManager->IsProTxWithCollateral(p.tx, o.n) || mnList.HasMNByCollateral(o)) {\n                vOutpts.emplace_back(o);\n            }\n        }\n    }\n}\n\n/** @} */ // end of Actions\n\nvoid CWallet::GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const {\n    AssertLockHeld(cs_wallet); // mapKeyMetadata\n    mapKeyBirth.clear();\n\n    // get birth times for keys with metadata\n    for (const auto& entry : mapKeyMetadata) {\n        if (entry.second.nCreateTime) {\n            mapKeyBirth[entry.first] = entry.second.nCreateTime;\n        }\n    }\n\n    // map in which we\'ll infer heights of other keys\n    CBlockIndex *pindexMax = chainActive[std::max(0, chainActive.Height() - 144)]; // the tip can be reorganized; use a 144-block safety margin\n    std::map<CKeyID, CBlockIndex*> mapKeyFirstBlock;\n    std::set<CKeyID> setKeys;\n    GetKeys(setKeys);\n    BOOST_FOREACH(const CKeyID &keyid, setKeys) {\n        if (mapKeyBirth.count(keyid) == 0)\n            mapKeyFirstBlock[keyid] = pindexMax;\n    }\n    setKeys.clear();\n\n    // if there are no such keys, we\'re done\n    if (mapKeyFirstBlock.empty())\n        return;\n\n    // find first block that affects those keys, if there are any left\n    std::vector<CKeyID> vAffected;\n    for (std::map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); it++) {\n        // iterate over all wallet transactions...\n        const CWalletTx &wtx = (*it).second;\n        BlockMap::const_iterator blit = mapBlockIndex.find(wtx.hashBlock);\n        if (blit != mapBlockIndex.end() && chainActive.Contains(blit->second)) {\n            // ... which are already in a block\n            int nHeight = blit->second->nHeight;\n            BOOST_FOREACH(const CTxOut &txout, wtx.tx->vout) {\n                // iterate over all their outputs\n                CAffectedKeysVisitor(*this, vAffected).Process(txout.scriptPubKey);\n                BOOST_FOREACH(const CKeyID &keyid, vAffected) {\n                    // ... and all their affected keys\n                    std::map<CKeyID, CBlockIndex*>::iterator rit = mapKeyFirstBlock.find(keyid);\n                    if (rit != mapKeyFirstBlock.end() && nHeight < rit->second->nHeight)\n                        rit->second = blit->second;\n                }\n                vAffected.clear();\n            }\n        }\n    }\n\n    // Extract block timestamps for those keys\n    for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)\n        mapKeyBirth[it->first] = it->second->GetBlockTime() - 7200; // block times can be 2h off\n}\n\nbool CWallet::AddDestData(const CTxDestination &dest, const std::string &key, const std::string &value)\n{\n    if (boost::get<CNoDestination>(&dest))\n        return false;\n\n    mapAddressBook[dest].destdata.insert(std::make_pair(key, value));\n    if (!fFileBacked)\n        return true;\n    return CWalletDB(strWalletFile).WriteDestData(CBitcoinAddress(dest).ToString(), key, value);\n}\n\nbool CWallet::EraseDestData(const CTxDestination &dest, const std::string &key)\n{\n    if (!mapAddressBook[dest].destdata.erase(key))\n        return false;\n    if (!fFileBacked)\n        return true;\n    return CWalletDB(strWalletFile).EraseDestData(CBitcoinAddress(dest).ToString(), key);\n}\n\nbool CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)\n{\n    mapAddressBook[dest].destdata.insert(std::make_pair(key, value));\n    return true;\n}\n\nbool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const\n{\n    std::map<CTxDestination, CAddressBookData>::const_iterator i = mapAddressBook.find(dest);\n    if(i != mapAddressBook.end())\n    {\n        CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);\n        if(j != i->second.destdata.end())\n        {\n            if(value)\n                *value = j->second;\n            return true;\n        }\n    }\n    return false;\n}\n\nstd::string CWallet::GetWalletHelpString(bool showDebug)\n{\n    std::string strUsage = HelpMessageGroup(_("Wallet options:"));\n    strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls"));\n    strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));\n    strUsage += HelpMessageOpt("-mintpoolsize=<n>", strprintf(_("Set mint pool size to <n> (default: %u)"), DEFAULT_MINTPOOL_SIZE));\n    strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),\n                                                               CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));\n    strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),\n                                                            CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));\n    strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),\n                                                            CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));\n    strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup"));\n    strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup"));\n    if (showDebug)\n        strUsage += HelpMessageOpt("-sendfreetransactions", strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), DEFAULT_SEND_FREE_TRANSACTIONS));\n    strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE));\n    strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET));\n    strUsage += HelpMessageOpt("-usehd", _("Use hierarchical deterministic key generation (HD) after BIP32. Only has effect during wallet creation/first start") + " " + strprintf(_("(default: %u)"), DEFAULT_USE_HD_WALLET));\n    strUsage += HelpMessageOpt("-usemnemonic", _("Use Mnemonic code for generating deterministic keys. Only has effect during wallet creation/first start") +\n                               " " + strprintf(_("(default: %u)"), DEFAULT_USE_MNEMONIC));\n    strUsage += HelpMessageOpt("-mnemonic=<text>", _("User defined mnemonic for HD wallet (bip39). Only has effect during wallet creation/first start (default: randomly generated)"));\n    strUsage += HelpMessageOpt("-mnemonicpassphrase=<text>", _("User defined mnemonic passphrase for HD wallet (BIP39). Only has effect during wallet creation/first start (default: empty string)"));\n    strUsage += HelpMessageOpt("-hdseed=<hex>", _("User defined seed for HD wallet (should be in hex). Only has effect during wallet creation/first start (default: randomly generated)"));\n    strUsage += HelpMessageOpt("-batching", _("In case of sync/reindex verifies sigma/lelantus proofs with batch verification, default: true"));\n    strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (default: %u)"), DEFAULT_WALLET_RBF));\n    strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup"));\n    strUsage += HelpMessageOpt("-wallet=<file>", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), DEFAULT_WALLET_DAT));\n    strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST));\n    strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)"));\n    strUsage += HelpMessageOpt("-zapwalletmints", _("Delete all Sigma mints and only recover those parts of the blockchain through -reindex on startup"));\n    strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") +\n                               " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)"));\n\n    if (showDebug)\n    {\n        strUsage += HelpMessageGroup(_("Wallet debugging/testing options:"));\n\n        strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE));\n        strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET));\n        strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB));\n        strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS));\n    }\n\n    return strUsage;\n}\n\nCWallet* CWallet::CreateWalletFromFile(const std::string walletFile)\n{\n    if (GetBoolArg("-zapwalletmints", false)) {\n        uiInterface.InitMessage(_("Zapping all Sigma mints from wallet..."));\n\n        CWallet *tempWallet = new CWallet(walletFile);\n        DBErrors nZapMintRet = tempWallet->ZapSigmaMints();\n        DBErrors nZapLelantusMintRet = tempWallet->ZapLelantusMints();\n        if (nZapMintRet != DB_LOAD_OK || nZapLelantusMintRet != DB_LOAD_OK) {\n            InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));\n            return NULL;\n        }\n\n        delete tempWallet;\n        tempWallet = NULL;\n    }\n\n    // needed to restore wallet transaction meta data after -zapwallettxes\n    std::vector<CWalletTx> vWtx;\n\n    if (GetBoolArg("-zapwallettxes", false)) {\n        uiInterface.InitMessage(_("Zapping all transactions from wallet..."));\n\n        CWallet *tempWallet = new CWallet(walletFile);\n        DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);\n        if (nZapWalletRet != DB_LOAD_OK) {\n            InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));\n            return NULL;\n        }\n\n        delete tempWallet;\n        tempWallet = NULL;\n    }\n\n    uiInterface.InitMessage(_("Loading wallet..."));\n\n    int64_t nStart = GetTimeMillis();\n    bool fFirstRun = true;\n    bool fRecoverMnemonic = false;\n    CWallet *walletInstance = new CWallet(walletFile);\n    pwalletMain = walletInstance;\n\n    DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun);\n    if (nLoadWalletRet != DB_LOAD_OK)\n    {\n        if (nLoadWalletRet == DB_CORRUPT) {\n            InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));\n            return NULL;\n        }\n        else if (nLoadWalletRet == DB_NONCRITICAL_ERROR)\n        {\n            InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"\n                                         " or address book entries might be missing or incorrect."),\n                walletFile));\n        }\n        else if (nLoadWalletRet == DB_TOO_NEW) {\n            InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));\n            return NULL;\n        }\n        else if (nLoadWalletRet == DB_NEED_REWRITE)\n        {\n            InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));\n            return NULL;\n        }\n        else {\n            InitError(strprintf(_("Error loading %s"), walletFile));\n            return NULL;\n        }\n    }\n\n    if (GetBoolArg("-upgradewallet", fFirstRun))\n    {\n        int nMaxVersion = GetArg("-upgradewallet", 0);\n        if (nMaxVersion == 0) // the -upgradewallet without argument case\n        {\n            LogPrintf("Performing wallet upgrade to %i\\n", FEATURE_LATEST);\n            nMaxVersion = CLIENT_VERSION;\n            walletInstance->SetMinVersion(FEATURE_LATEST); // permanently upgrade the wallet immediately\n        }\n        else\n            LogPrintf("Allowing wallet upgrade up to %i\\n", nMaxVersion);\n        if (nMaxVersion < walletInstance->GetVersion())\n        {\n            InitError(_("Cannot downgrade wallet"));\n            return NULL;\n        }\n        walletInstance->SetMaxVersion(nMaxVersion);\n    }\n\n    if (fFirstRun)\n    {\n        // Create new keyUser and set as default key\n        if (GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET) && !walletInstance->IsHDEnabled()) {\n            if(GetBoolArg("-usemnemonic", DEFAULT_USE_MNEMONIC)) {\n                if (GetArg("-mnemonicpassphrase", "").size() > 256) {\n                    throw std::runtime_error(std::string(__func__) + ": Mnemonic passphrase is too long, must be at most 256 characters");\n                }\n                // generate a new HD chain\n                walletInstance->GenerateNewMnemonic();\n                walletInstance->SetMinVersion(FEATURE_HD);\n                /* set rescan to true.\n                 * if blockchain data is not present it has no effect, but it\'s needed for a mnemonic restore where chain data is present.\n                 */\n                SoftSetBoolArg("-rescan", true);\n                fRecoverMnemonic = true;\n            }else{\n            // generate a new master key\n            CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey();\n            if (!walletInstance->SetHDMasterKey(masterPubKey, CHDChain().VERSION_WITH_BIP44))\n                throw std::runtime_error(std::string(__func__) + ": Storing master key failed");\n            }\n        }\n        CPubKey newDefaultKey;\n        if (walletInstance->GetKeyFromPool(newDefaultKey)) {\n            walletInstance->SetDefaultKey(newDefaultKey);\n            if (!walletInstance->SetAddressBook(walletInstance->vchDefaultKey.GetID(), "", "receive")) {\n                InitError(_("Cannot write default address") += "\\n");\n                return NULL;\n            }\n        }\n\n        walletInstance->SetBestChain(chainActive.GetLocator());\n    }\n    else if (IsArgSet("-usehd")) {\n        bool useHD = GetBoolArg("-usehd", DEFAULT_USE_HD_WALLET);\n        if (walletInstance->IsHDEnabled() && !useHD) {\n            InitError(strprintf(_("Error loading %s: You can\'t disable HD on a already existing HD wallet"), walletFile));\n            return NULL;\n        }\n        if (!walletInstance->IsHDEnabled() && useHD) {\n            InitError(strprintf(_("Error loading %s: You can\'t enable HD on a already existing non-HD wallet"), walletFile));\n            return NULL;\n        }\n    }\n\n    LogPrintf(" wallet      %15dms\\n", GetTimeMillis() - nStart);\n    if (pwalletMain->IsHDSeedAvailable()) {\n        walletInstance->zwallet = std::make_unique<CHDMintWallet>(pwalletMain->strWalletFile);\n    }\n\n    walletInstance->bip47wallet = std::make_shared<bip47::CWallet>(walletInstance->vchDefaultKey.GetHash());\n    walletInstance->LoadBip47Wallet();\n    if (fFirstRun) {\n        int const defaultPcodeNumber = std::stoi(GetArg("-defaultrapaddressnumber", "0"));\n        for(int i = 0; i < defaultPcodeNumber; ++i)\n            walletInstance->GeneratePcode("Autogenerated RAP address " + std::to_string(i));\n    }\n\n    RegisterValidationInterface(walletInstance);\n\n    // Try to top up keypool. No-op if the wallet is locked.\n    walletInstance->TopUpKeyPool();\n\n    CBlockIndex *pindexRescan = chainActive.Tip();\n    if (GetBoolArg("-rescan", false))\n        pindexRescan = chainActive.Genesis();\n    else\n    {\n        CWalletDB walletdb(walletFile);\n        CBlockLocator locator;\n        if (walletdb.ReadBestBlock(locator))\n            pindexRescan = FindForkInGlobalIndex(chainActive, locator);\n        else\n            pindexRescan = chainActive.Genesis();\n    }\n    if (chainActive.Tip() && chainActive.Tip() != pindexRescan)\n    {\n        //We can\'t rescan beyond non-pruned blocks, stop and throw an error\n        //this might happen if a user uses a old wallet within a pruned node\n        // or if he ran -disablewallet for a longer time, then decided to re-enable\n        if (fPruneMode)\n        {\n            CBlockIndex *block = chainActive.Tip();\n            while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block)\n                block = block->pprev;\n\n            if (pindexRescan != block) {\n                InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));\n                return NULL;\n            }\n        }\n\n        uiInterface.InitMessage(_("Rescanning..."));\n        LogPrintf("Rescanning last %i blocks (from block %i)...\\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight);\n        nStart = GetTimeMillis();\n        walletInstance->ScanForWalletTransactions(pindexRescan, true, fRecoverMnemonic);\n        LogPrintf(" rescan      %15dms\\n", GetTimeMillis() - nStart);\n        walletInstance->SetBestChain(chainActive.GetLocator());\n        CWalletDB::IncrementUpdateCounter();\n\n        // Restore wallet transaction metadata after -zapwallettxes=1\n        if (GetBoolArg("-zapwallettxes", false))\n        {\n            std::string zwtValue = GetArg("-zapwallettxes", "1");\n            if(zwtValue != "2") {\n                CWalletDB walletdb(walletFile);\n\n                BOOST_FOREACH(const CWalletTx &wtxOld, vWtx)\n                {\n                    uint256 hash = wtxOld.GetHash();\n                    std::map<uint256, CWalletTx>::iterator mi = walletInstance->mapWallet.find(hash);\n                    if (mi != walletInstance->mapWallet.end()) {\n                        const CWalletTx *copyFrom = &wtxOld;\n                        CWalletTx *copyTo = &mi->second;\n                        copyTo->mapValue = copyFrom->mapValue;\n                        copyTo->vOrderForm = copyFrom->vOrderForm;\n                        copyTo->nTimeReceived = copyFrom->nTimeReceived;\n                        copyTo->nTimeSmart = copyFrom->nTimeSmart;\n                        copyTo->fFromMe = copyFrom->fFromMe;\n                        copyTo->strFromAccount = copyFrom->strFromAccount;\n                        copyTo->nOrderPos = copyFrom->nOrderPos;\n                        walletdb.WriteTx(*copyTo);\n                    }\n                }\n            }\n        }\n    }\n\n    // recover addressbook\n    if (fFirstRun)\n    {\n        for (std::map<uint256, CWalletTx>::iterator it = walletInstance->mapWallet.begin(); it != walletInstance->mapWallet.end(); ++it) {\n            for (uint32_t i = 0; i < (*it).second.tx->vout.size(); i++) {\n                const auto& txout = (*it).second.tx->vout[i];\n                if(txout.scriptPubKey.IsMint() || (*it).second.changes.count(i))\n                    continue;\n                if (!walletInstance->IsMine(txout))\n                    continue;\n                CTxDestination addr;\n                if(!ExtractDestination(txout.scriptPubKey, addr))\n                    continue;\n                if (walletInstance->mapAddressBook.count(addr) == 0)\n                    walletInstance->SetAddressBook(addr, "", "receive");\n            }\n        }\n    }\n\n    walletInstance->SetBroadcastTransactions(GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST));\n\n    {\n        LOCK(walletInstance->cs_wallet);\n        LogPrintf("setKeyPool.size() = %u\\n",      walletInstance->GetKeyPoolSize());\n        LogPrintf("mapWallet.size() = %u\\n",       walletInstance->mapWallet.size());\n        LogPrintf("mapAddressBook.size() = %u\\n",  walletInstance->mapAddressBook.size());\n    }\n\n    return walletInstance;\n}\n\nbool CWallet::InitLoadWallet()\n{\n    if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) {\n        pwalletMain = NULL;\n        LogPrintf("Wallet disabled!\\n");\n        return true;\n    }\n\n    std::string walletFile = GetArg("-wallet", DEFAULT_WALLET_DAT);\n\n    if (walletFile.find_first_of("/\\\\") != std::string::npos) {\n        return InitError(_("-wallet parameter must only specify a filename (not a path)"));\n    } else if (SanitizeString(walletFile, SAFE_CHARS_FILENAME) != walletFile) {\n        return InitError(_("Invalid characters in -wallet filename"));\n    }\n\n    CWallet * const pwallet = CreateWalletFromFile(walletFile);\n    if (!pwallet) {\n        return false;\n    }\n    pwalletMain = pwallet;\n\n    return true;\n}\n\nstd::atomic<bool> CWallet::fFlushThreadRunning(false);\n\nvoid CWallet::postInitProcess(boost::thread_group& threadGroup)\n{\n    // Add wallet transactions that aren\'t already in a block to mempool\n    // Do this here as mempool requires genesis block to be loaded\n    ReacceptWalletTransactions();\n\n    // Run a thread to flush wallet periodically\n    if (!CWallet::fFlushThreadRunning.exchange(true)) {\n        threadGroup.create_thread(ThreadFlushWalletDB);\n    }\n}\n\nbool CWallet::ParameterInteraction()\n{\n    if (GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET))\n        return true;\n\n    if (GetBoolArg("-blocksonly", DEFAULT_BLOCKSONLY) && SoftSetBoolArg("-walletbroadcast", false)) {\n        LogPrintf("%s: parameter interaction: -blocksonly=1 -> setting -walletbroadcast=0\\n", __func__);\n    }\n\n    if (GetBoolArg("-salvagewallet", false) && SoftSetBoolArg("-rescan", true)) {\n        // Rewrite just private keys: rescan to find transactions\n        LogPrintf("%s: parameter interaction: -salvagewallet=1 -> setting -rescan=1\\n", __func__);\n    }\n\n    // -zapwallettx implies a rescan\n    if (GetBoolArg("-zapwallettxes", false) && SoftSetBoolArg("-rescan", true)) {\n        LogPrintf("%s: parameter interaction: -zapwallettxes=<mode> -> setting -rescan=1\\n", __func__);\n    }\n\n    if (GetBoolArg("-sysperms", false))\n        return InitError("-sysperms is not allowed in combination with enabled wallet functionality");\n    if (GetArg("-prune", 0) && GetBoolArg("-rescan", false))\n        return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));\n\n    if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)\n        InitWarning(AmountHighWarn("-minrelaytxfee") + " " +\n                    _("The wallet will avoid paying less than the minimum relay fee."));\n\n    if (IsArgSet("-mintxfee"))\n    {\n        CAmount n = 0;\n        if (!ParseMoney(GetArg("-mintxfee", ""), n) || 0 == n)\n            return InitError(AmountErrMsg("mintxfee", GetArg("-mintxfee", "")));\n        if (n > HIGH_TX_FEE_PER_KB)\n            InitWarning(AmountHighWarn("-mintxfee") + " " +\n                        _("This is the minimum transaction fee you pay on every transaction."));\n        CWallet::minTxFee = CFeeRate(n);\n    }\n    if (IsArgSet("-fallbackfee"))\n    {\n        CAmount nFeePerK = 0;\n        if (!ParseMoney(GetArg("-fallbackfee", ""), nFeePerK))\n            return InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: \'%s\'"), GetArg("-fallbackfee", "")));\n        /*\n        if (nFeePerK > HIGH_TX_FEE_PER_KB)\n            InitWarning(AmountHighWarn("-fallbackfee") + " " +\n                        _("This is the transaction fee you may pay when fee estimates are not available."));\n        */\n        CWallet::fallbackFee = CFeeRate(nFeePerK);\n    }\n    if (IsArgSet("-paytxfee"))\n    {\n        CAmount nFeePerK = 0;\n        if (!ParseMoney(GetArg("-paytxfee", ""), nFeePerK))\n            return InitError(AmountErrMsg("paytxfee", GetArg("-paytxfee", "")));\n        /*\n        if (nFeePerK > HIGH_TX_FEE_PER_KB)\n            InitWarning(AmountHighWarn("-paytxfee") + " " +\n                        _("This is the transaction fee you will pay if you send a transaction."));\n        */\n\n        payTxFee = CFeeRate(nFeePerK, 1000);\n        if (payTxFee < ::minRelayTxFee)\n        {\n            return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: \'%s\' (must be at least %s)"),\n                                       GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));\n        }\n    }\n    if (IsArgSet("-maxtxfee"))\n    {\n        CAmount nMaxFee = 0;\n        if (!ParseMoney(GetArg("-maxtxfee", ""), nMaxFee))\n            return InitError(AmountErrMsg("maxtxfee", GetArg("-maxtxfee", "")));\n        /*\n        if (nMaxFee > HIGH_MAX_TX_FEE)\n            InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));\n        */\n        maxTxFee = nMaxFee;\n        if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)\n        {\n            return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: \'%s\' (must be at least the minrelay fee of %s to prevent stuck transactions)"),\n                                       GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));\n        }\n    }\n\n    if (IsArgSet("-mininput"))\n    {\n        if (!ParseMoney(GetArg("-mininput", ""), nMinimumInputValue))\n            return InitError(strprintf(_("Invalid amount for -mininput=<amount>: \'%s\'"), GetArg("-mininput", "").c_str()));\n    }\n\n    nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);\n    bSpendZeroConfChange = GetBoolArg("-spendzeroconfchange", DEFAULT_SPEND_ZEROCONF_CHANGE);\n    fSendFreeTransactions = GetBoolArg("-sendfreetransactions", DEFAULT_SEND_FREE_TRANSACTIONS);\n    fWalletRbf = GetBoolArg("-walletrbf", DEFAULT_WALLET_RBF);\n\n    if (fSendFreeTransactions && GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)\n        return InitError("Creation of free transactions with their relay disabled is not supported.");\n\n    return true;\n}\n\nbool CWallet::BackupWallet(const std::string& strDest)\n{\n    if (!fFileBacked)\n        return false;\n    while (true)\n    {\n        {\n            LOCK(bitdb.cs_db);\n            if (!bitdb.mapFileUseCount.count(strWalletFile) || bitdb.mapFileUseCount[strWalletFile] == 0)\n            {\n                // Flush log data to the dat file\n                bitdb.CloseDb(strWalletFile);\n                bitdb.CheckpointLSN(strWalletFile);\n                bitdb.mapFileUseCount.erase(strWalletFile);\n\n                // Copy wallet file\n                boost::filesystem::path pathSrc = GetDataDir() / strWalletFile;\n                boost::filesystem::path pathDest(strDest);\n                if (boost::filesystem::is_directory(pathDest))\n                    pathDest /= strWalletFile;\n\n                try {\n#if BOOST_VERSION >= 104000\n                    boost::filesystem::copy_file(pathSrc, pathDest, boost::filesystem::copy_option::overwrite_if_exists);\n#else\n                    boost::filesystem::copy_file(pathSrc, pathDest);\n#endif\n                    LogPrintf("copied %s to %s\\n", strWalletFile, pathDest.string());\n                    return true;\n                } catch (const boost::filesystem::filesystem_error& e) {\n                    LogPrintf("error copying %s to %s - %s\\n", strWalletFile, pathDest.string(), e.what());\n                    return false;\n                }\n            }\n        }\n        MilliSleep(100);\n    }\n    return false;\n}\n\nbip47::CPaymentCode CWallet::GeneratePcode(std::string const & label)\n{\n    if (!bip47wallet)\n        throw WalletError("BIP47 wallet was not created during the initialization");\n\n    bip47::CAccountReceiver & newAcc = bip47wallet->createReceivingAccount(label);\n    {\n        bip47::MyAddrContT addrs = newAcc.getMyNextAddresses();\n        LOCK(cs_wallet);\n        for(bip47::MyAddrContT::value_type const & addr : addrs) {\n            AddKey(addr.second);\n        }\n    }\n    CWalletDB(strWalletFile).WriteBip47Account(newAcc);\n    NotifyPcodeCreated(bip47::CPaymentCodeDescription(newAcc.getAccountNum(), newAcc.getMyPcode(), newAcc.getLabel(), newAcc.getMyPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Receiver));\n    return newAcc.getMyPcode();\n}\n\nCWalletTx CWallet::PrepareAndSendNotificationTx(bip47::CPaymentCode const & theirPcode)\n{\n    bip47::CPaymentChannel pchannel = SetupPchannel(theirPcode);\n\n    CWalletTx wtxNew;\n\n    if (GetBroadcastTransactions() && !g_connman) {\n        throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");\n    }\n\n    CBitcoinAddress const notifAddr = pchannel.getTheirPcode().getNotificationAddress();\n\n    std::vector<CRecipient> recipients;\n    std::vector<CAmount> newMints;\n\n    CRecipient receiver;\n    receiver.scriptPubKey = GetScriptForDestination(notifAddr.Get());\n    receiver.nAmount = bip47::NotificationTxValue;\n    receiver.fSubtractFeeFromAmount = false;\n\n    recipients.emplace_back(receiver);\n    CScript opReturnScript = CScript() << OP_RETURN << std::vector<unsigned char>(80); // Passing empty array to calc fees\n    recipients.push_back({opReturnScript, 0, false});\n\n    auto throwSigma =\n        [](){throw std::runtime_error(std::string("There are unspent Sigma coins in your wallet. Using Sigma coins for BIP47 is not supported. Please spend your Sigma coins before establishing a BIP47 channel."));};\n\n    try {\n        std::vector<CLelantusEntry> spendCoins;\n        std::vector<CSigmaEntry> sigmaSpendCoins;\n        std::vector<CHDMint> mintCoins;\n        CAmount fee;\n\n        wtxNew = CreateLelantusJoinSplitTransaction(recipients, fee, newMints, spendCoins, sigmaSpendCoins, mintCoins, nullptr,\n                [&pchannel, &throwSigma](CTxOut & out, LelantusJoinSplitBuilder const & builder) {\n                    if(out.scriptPubKey[0] == OP_RETURN) {\n                        CKey spendPrivKey;\n                        if (builder.spendCoins.empty())\n                            throwSigma();\n                        spendPrivKey.Set(builder.spendCoins[0].ecdsaSecretKey.begin(), builder.spendCoins[0].ecdsaSecretKey.end(), false);\n                        CDataStream ds(SER_NETWORK, 0);\n                        ds << builder.spendCoins[0].serialNumber;\n                        bip47::Bytes const pcode = pchannel.getMaskedPayload((unsigned char const *)ds.vch.data(), ds.vch.size(), spendPrivKey);\n                        out.scriptPubKey = CScript() << OP_RETURN << pcode;\n                    }\n                });\n\n        if (!sigmaSpendCoins.empty())\n            throwSigma();\n\n        if (spendCoins.empty())\n            throw std::runtime_error(std::string("Cannot create a Lelantus spend to address: " + notifAddr.ToString()).c_str());\n\n        CommitLelantusTransaction(wtxNew, spendCoins, sigmaSpendCoins, mintCoins);\n        LogBip47("Paymentcode %s was sent to notification address: %s\\n", pchannel.getMyPcode().toString().c_str(), notifAddr.ToString().c_str() );\n    }\n    catch (const InsufficientFunds& e)\n    {\n        throw e;\n    }\n    catch (const std::exception& e)\n    {\n        throw WalletError(e.what());\n    }\n\n    SetNotificationTxId(theirPcode, wtxNew.GetHash());\n    return wtxNew;\n}\n\nstd::vector<bip47::CPaymentCodeDescription> CWallet::ListPcodes()\n{\n    std::vector<bip47::CPaymentCodeDescription> result;\n    if (!bip47wallet)\n        return result;\n\n    bip47wallet->enumerateReceivers(\n        [&result](bip47::CAccountReceiver const & acc)->bool\n        {\n            result.emplace_back(acc.getAccountNum(), acc.getMyPcode(), acc.getLabel(), acc.getMyNotificationAddress(), bip47::CPaymentCodeSide::Receiver);\n            return true;\n        }\n    );\n    return result;\n}\n\nbip47::CPaymentChannel & CWallet::SetupPchannel(bip47::CPaymentCode const & theirPcode)\n{\n    if (!bip47wallet)\n        throw WalletError("BIP47 wallet was not created during the initialization");\n\n    bip47::CAccountSender & sender = bip47wallet->provideSendingAccount(theirPcode);\n    CWalletDB(strWalletFile).WriteBip47Account(sender);\n    return sender.getPaymentChannel();\n}\n\nvoid CWallet::SetNotificationTxId(bip47::CPaymentCode const & theirPcode, uint256 const & txid)\n{\n    if (!bip47wallet)\n        throw WalletError("BIP47 wallet was not created during the initialization");\n\n    bip47::CAccountSender & sender = bip47wallet->provideSendingAccount(theirPcode);\n    sender.setNotificationTxId(txid);\n    CWalletDB(strWalletFile).WriteBip47Account(sender);\n}\n\nnamespace {\nCBitcoinAddress HandleTheirNextAddress(bip47::CWallet & wallet, std::string const & strWalletFile, bip47::CPaymentCode const & theirPcode, bool storeNextAddress)\n{\n    boost::optional<bip47::CAccountSender*> existingAcc;\n    wallet.enumerateSenders(\n        [&theirPcode, &existingAcc](bip47::CAccountSender & acc)->bool\n        {\n            if(acc.getTheirPcode() == theirPcode) {\n                existingAcc.emplace(&acc);\n                return false;\n            }\n            return true;\n        }\n    );\n    if(!existingAcc)\n        throw std::runtime_error("There is no account setup for payment code " + theirPcode.toString());\n    CBitcoinAddress result;\n    if(storeNextAddress)\n    {\n        result = existingAcc.get()->generateTheirNextSecretAddress();\n        LogBip47("Sending to secret address: %s\\n", result.ToString());\n    } else {\n        result = existingAcc.get()->getTheirNextSecretAddress();\n    }\n    CWalletDB(strWalletFile).WriteBip47Account(*existingAcc.get());\n    return result;\n}\n}\n\nCBitcoinAddress CWallet::GetTheirNextAddress(bip47::CPaymentCode const & theirPcode) const\n{\n    if (!bip47wallet)\n        throw WalletError("BIP47 wallet was not created during the initialization");\n\n    return HandleTheirNextAddress(*bip47wallet, strWalletFile, theirPcode, false);\n}\n\nCBitcoinAddress CWallet::GenerateTheirNextAddress(bip47::CPaymentCode const & theirPcode)\n{\n    if (!bip47wallet)\n        throw WalletError("BIP47 wallet was not created during the initialization");\n\n    return HandleTheirNextAddress(*bip47wallet, strWalletFile, theirPcode, true);\n}\n\nvoid CWallet::LoadBip47Wallet()\n{\n    CWalletDB(strWalletFile).LoadBip47Accounts(*bip47wallet);\n}\n\nstd::shared_ptr<bip47::CWallet const> CWallet::GetBip47Wallet() const\n{\n    return bip47wallet;\n}\n\nboost::optional<bip47::CPaymentCodeDescription> CWallet::FindPcode(bip47::CPaymentCode const & pcode) const\n{\n    boost::optional<bip47::CPaymentCodeDescription> result;\n    if (!bip47wallet)\n        return result;\n\n    bip47wallet->enumerateReceivers(\n        [&pcode, &result](bip47::CAccountReceiver & rec)->bool\n        {\n            if(rec.getMyPcode() == pcode) {\n                result.emplace(rec.getAccountNum(), rec.getMyPcode(), rec.getLabel(), rec.getMyPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Receiver);\n                return false;\n            }\n\n            for(bip47::CPaymentChannel const & channel : rec.getPchannels()) {\n                if(channel.getTheirPcode() == pcode) {\n                    result.emplace(rec.getAccountNum(), rec.getMyPcode(), rec.getLabel(), rec.getMyPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Receiver);\n                    return false;\n                }\n            }\n            return true;\n        }\n    );\n    bip47wallet->enumerateSenders(\n        [&pcode, &result, this](bip47::CAccountSender & sender)->bool\n        {\n            if(sender.getTheirPcode() == pcode) {\n                std::string label = GetSendingPcodeLabel(sender.getTheirPcode());\n                result.emplace(sender.getAccountNum(), sender.getTheirPcode(), label, sender.getTheirPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Sender);\n                return false;\n            }\n            return true;\n        }\n    );\n    return result;\n}\n\nboost::optional<bip47::CPaymentCodeDescription> CWallet::FindPcode(CBitcoinAddress const & address) const\n{\n    boost::optional<bip47::CPaymentCodeDescription> result;\n    if (!bip47wallet)\n        return result;\n\n    bip47wallet->enumerateReceivers(\n        [&address, &result](bip47::CAccountReceiver & rec)->bool\n        {\n            bip47::MyAddrContT addrs = rec.getMyUsedAddresses();\n            if (std::find_if(addrs.begin(), addrs.end(), bip47::FindByAddress(address)) != addrs.end())\n            {\n                result.emplace(rec.getAccountNum(), rec.getMyPcode(), rec.getLabel(), rec.getMyPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Receiver);\n                return false;\n            }\n            addrs = rec.getMyNextAddresses();\n            if (std::find_if(addrs.begin(), addrs.end(), bip47::FindByAddress(address)) != addrs.end())\n            {\n                result.emplace(rec.getAccountNum(), rec.getMyPcode(), rec.getLabel(), rec.getMyPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Receiver);\n                return false;\n            }\n            return true;\n        }\n    );\n    bip47wallet->enumerateSenders(\n        [&address, &result, this](bip47::CAccountSender & sender)->bool\n        {\n            bip47::TheirAddrContT addrs = sender.getTheirUsedAddresses();\n            if (std::find(addrs.begin(), addrs.end(), address) != addrs.end())\n            {\n                std::string label = GetSendingPcodeLabel(sender.getTheirPcode());\n                result.emplace(sender.getAccountNum(), sender.getTheirPcode(), label, sender.getTheirPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Sender);\n                return false;\n            }\n            if (address == sender.getTheirNextSecretAddress() || address == sender.getTheirPcode().getNotificationAddress())\n            {\n                std::string label = GetSendingPcodeLabel(sender.getTheirPcode());\n                result.emplace(sender.getAccountNum(), sender.getTheirPcode(), label, sender.getTheirPcode().getNotificationAddress(), bip47::CPaymentCodeSide::Sender);\n                return false;\n            }\n            return true;\n        }\n    );\n    return result;\n}\n\nbip47::CAccountReceiver const * CWallet::AddressUsed(CBitcoinAddress const & address)\n{\n    bip47::CAccountReceiver const * result = nullptr;\n    if(!bip47wallet)\n        return result;\n\n    bip47wallet->enumerateReceivers(\n        [&address, &result](bip47::CAccountReceiver & rec)->bool\n        {\n            bip47::MyAddrContT addrs = rec.getMyNextAddresses();\n            if (std::find_if(addrs.begin(), addrs.end(), bip47::FindByAddress(address)) != addrs.end())\n            {\n                rec.addressUsed(address);\n                result = &rec;\n                return false;\n            }\n            return true;\n        }\n    );\n    if (result)\n        CWalletDB(strWalletFile).WriteBip47Account(*result);\n    return result;\n}\n\nvoid CWallet::HandleBip47Transaction(CWalletTx const & wtx)\n{\n    bip47::Bytes masked = bip47::utils::GetMaskedPcode(wtx.tx);\n    CKey key;\n    bip47::CAccountReceiver * accFound = nullptr;\n    int nRequired = 0;\n    std::vector<CTxDestination> addresses;\n    txnouttype typeRet = TX_NONSTANDARD;\n    std::vector<CTxIn>::const_iterator ijsplit;\n    std::vector<CTxOut>::const_iterator iregout;\n    bool success = false;\n\n    if (masked.empty()) goto notifTxExit;\n\n    ijsplit = std::find_if(wtx.tx->vin.begin(), wtx.tx->vin.end(), [](CTxIn const & in){ return in.scriptSig.IsLelantusJoinSplit(); });\n    if(ijsplit == wtx.tx->vin.end()) {\n        LogBip47("Joinsplit input was not found in a potential notification tx: %s\\n", wtx.tx->GetHash().ToString());\n        goto notifTxExit;\n    }\n\n    iregout = std::find_if(wtx.tx->vout.begin(), wtx.tx->vout.end(), [](CTxOut const & out){ return out.scriptPubKey[0] != OP_RETURN && !out.scriptPubKey.IsLelantusJMint(); });\n    if(iregout == wtx.tx->vout.end()) {\n        LogBip47("Regular out was not found in a potential notification tx: %s\\n", wtx.tx->GetHash().ToString());\n        goto notifTxExit;\n    }\n    if(!ExtractDestinations(iregout->scriptPubKey, typeRet, addresses, nRequired)) {\n        LogBip47("Cannot extract destinations for tx: %s\\n", wtx.tx->GetHash().ToString());\n        goto notifTxExit;\n    }\n    bip47wallet->enumerateReceivers(\n        [&key, &addresses, &accFound](bip47::CAccountReceiver & acc)->bool\n        {\n            for (CBitcoinAddress addr : addresses) {\n                if(acc.getMyNotificationAddress() == addr) {\n                    key = acc.getMyNextAddresses()[0].second;\n                    accFound = &acc;\n                    return false;\n                }\n            }\n            return true;\n        }\n    );\n    if(!accFound) {\n        LogBip47("There was no account set up to receive payments on address: %s\\n", CBitcoinAddress(addresses[0]).ToString());\n        goto notifTxExit;\n    }\n    if(!accFound->acceptMaskedPayload(masked, *wtx.tx)){\n        LogBip47("Could not accept this masked payload: %s\\n", HexStr(masked));\n        goto notifTxExit;\n    }\n    success = true;\nnotifTxExit:\n    if (success) {\n        LogBip47("The payment code has been accepted: %s\\n", accFound->lastPcode().toString());\n        HandleSecretAddresses(*this, *accFound);\n        CWalletDB(strWalletFile).WriteBip47Account(*accFound);\n        LockCoin(COutPoint(wtx.tx->GetHash(), std::distance(wtx.tx->vout.begin(), iregout))); //Locking the notif tx output to be spent only manually\n    } else {\n        // Checking if it uses a bip47 address\n        for (CTxOut const & out : wtx.tx->vout) {\n            std::vector<CTxDestination> addresses;\n            txnouttype typeRet = TX_NONSTANDARD;\n            int nRequired = 0;\n            if (ExtractDestinations(out.scriptPubKey, typeRet, addresses, nRequired)) {\n                for (CBitcoinAddress addr : addresses) {\n                    bip47::CAccountReceiver const * rec = AddressUsed(addr);\n                    if (rec) {\n                        HandleSecretAddresses(*this, *rec);\n                    }\n                }\n            }\n        }\n    }\n}\n\nvoid CWallet::LabelSendingPcode(bip47::CPaymentCode const & pcode_, std::string const & label, bool remove)\n{\n    std::string const pcodeLbl = bip47::PcodeLabel() + pcode_.toString();\n    if (label.empty())\n        remove = true;\n    CWalletDB walletDb(strWalletFile);\n    if (remove) {\n        walletDb.EraseKV(pcodeLbl);\n        LOCK(cs_wallet);\n        mapCustomKeyValues.erase(pcodeLbl);\n    } else {\n        std::multimap<std::string, std::string>::iterator iter = mapCustomKeyValues.find(pcodeLbl);\n        if (iter == mapCustomKeyValues.end()) {\n            LOCK(cs_wallet);\n            mapCustomKeyValues.insert(std::make_pair(pcodeLbl, label));\n        } else {\n            if (iter->second == label)\n                return;\n            iter->second = label;\n        }\n        walletDb.EraseKV(pcodeLbl);\n        walletDb.WriteKV(pcodeLbl, label);\n    }\n    NotifyPcodeLabeled(pcode_.toString(), label, remove);\n}\n\nstd::string CWallet::GetSendingPcodeLabel(bip47::CPaymentCode const & pcode) const\n{\n    std::string const pcodeLbl = bip47::PcodeLabel() + pcode.toString();\n    LOCK(cs_wallet);\n    std::multimap<std::string, std::string>::const_iterator iter = mapCustomKeyValues.find(pcodeLbl);\n    if(iter == mapCustomKeyValues.end())\n        return "";\n    return iter->second;\n}\n\nvoid CWallet::LabelReceivingPcode(bip47::CPaymentCode const & pcode, std::string const & label)\n{\n    if (!bip47wallet)\n        return;\n\n    LOCK(cs_wallet);\n    bip47::CAccountReceiver * result = nullptr;\n    bip47wallet->enumerateReceivers(\n        [&result, &pcode](bip47::CAccountReceiver & rec)->bool\n        {\n            if(rec.getMyPcode() == pcode)\n            {\n                result = &rec;\n                return false;\n            }\n            return true;\n        }\n    );\n    if(!result)\n        return;\n    result->setLabel(label);\n    CWalletDB(strWalletFile).WriteBip47Account(*result);\n}\n\nsize_t CWallet::SetUsedAddressNumber(bip47::CPaymentCode const & pcode, size_t number)\n{\n    boost::optional<size_t> resultSnd, resutRec;\n    bip47wallet->enumerateSenders(\n        [&pcode, &number, &resultSnd](bip47::CAccountSender & sender)->bool\n        {\n            if(sender.getTheirPcode() == pcode) {\n                resultSnd.emplace(sender.setTheirUsedAddressNumber(number));\n                return false;\n            }\n            return true;\n        }\n    );\n\n    bip47::CAccountReceiver * receiver;\n    bip47wallet->enumerateReceivers(\n        [&pcode, &number, &resutRec, &receiver](bip47::CAccountReceiver & rec)->bool\n        {\n            resutRec = rec.setMyUsedAddressNumber(pcode, number);\n            if(resutRec) {\n                receiver = &rec;\n                return false;\n            }\n            return true;\n        }\n    );\n    if(resutRec) {\n        HandleSecretAddresses(*this, *receiver);\n        return *resutRec;\n    }\n    if(resultSnd)\n        return *resultSnd;\n    return 0;\n}\n\nvoid CWallet::NotifyTransactionLock(const CTransaction &tx)\n{\n    LOCK(cs_wallet);\n    // Only notify UI if this transaction is in this wallet\n    std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(tx.GetHash());\n    if (mi != mapWallet.end()){\n        NotifyISLockReceived();\n    }\n}\n\nvoid CWallet::NotifyChainLock(const CBlockIndex* pindexChainLock)\n{\n    NotifyChainLockReceived(pindexChainLock->nHeight);\n}\n\n\n/******************************************************************************/\n/*                                                                            */\n/*                            CKeyPool                                        */\n/*                                                                            */\n/******************************************************************************/\n\n\nCKeyPool::CKeyPool()\n{\n    nTime = GetTime();\n}\n\nCKeyPool::CKeyPool(const CPubKey& vchPubKeyIn)\n{\n    nTime = GetTime();\n    vchPubKey = vchPubKeyIn;\n}\n\nCWalletKey::CWalletKey(int64_t nExpires)\n{\n    nTimeCreated = (nExpires ? GetTime() : 0);\n    nTimeExpires = nExpires;\n}\n\nvoid CMerkleTx::SetMerkleBranch(const CBlockIndex* pindex, int posInBlock)\n{\n    // Update the tx\'s hashBlock\n    hashBlock = pindex->GetBlockHash();\n\n    // set the position of the transaction in the block\n    nIndex = posInBlock;\n}\n\nint CMerkleTx::GetDepthInMainChain(const CBlockIndex *&pindexRet, bool enableIX) const {\n    int nResult;\n\n    if (hashUnset())\n        nResult = 0;\n    else {\n        AssertLockHeld(cs_main);\n\n        // Find the block it claims to be in\n        BlockMap::iterator mi = mapBlockIndex.find(hashBlock);\n        if (mi == mapBlockIndex.end())\n            nResult = 0;\n        else {\n            CBlockIndex *pindex = (*mi).second;\n            if (!pindex || !chainActive.Contains(pindex))\n                nResult = 0;\n            else {\n                pindexRet = pindex;\n                nResult = ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);\n\n                if (nResult == 0 && !mempool.exists(GetHash()))\n                    return -1; // Not in chain, not in mempool\n            }\n        }\n    }\n\n    return nResult;\n}\n\nbool CMerkleTx::IsLockedByLLMQInstantSend() const\n{\n    return llmq::quorumInstantSendManager->IsLocked(GetHash());\n}\n\nbool CMerkleTx::IsChainLocked() const\n{\n    AssertLockHeld(cs_main);\n    BlockMap::iterator mi = mapBlockIndex.find(hashBlock);\n    if (mi != mapBlockIndex.end() && mi->second != nullptr) {\n        return llmq::chainLocksHandler->HasChainLock(mi->second->nHeight, hashBlock);\n    }\n    return false;\n}\n\nint CMerkleTx::GetDepthInMainChain(const CBlockIndex* &pindexRet) const\n{\n    if (hashUnset())\n        return 0;\n\n    AssertLockHeld(cs_main);\n\n    // Find the block it claims to be in\n    BlockMap::iterator mi = mapBlockIndex.find(hashBlock);\n    if (mi == mapBlockIndex.end())\n        return 0;\n    CBlockIndex* pindex = (*mi).second;\n    if (!pindex || !chainActive.Contains(pindex))\n        return 0;\n\n    pindexRet = pindex;\n    return ((nIndex == -1) ? (-1) : 1) * (chainActive.Height() - pindex->nHeight + 1);\n}\n\nint CMerkleTx::GetBlocksToMaturity() const\n{\n    if (!IsCoinBase())\n        return 0;\n    return std::max(0, (COINBASE_MATURITY+1) - GetDepthInMainChain());\n}\n\n\nbool CMerkleTx::AcceptToMemoryPool(const CAmount &nAbsurdFee, CValidationState &state)\n{\n    if (GetBoolArg("-dandelion", true)) {\n        bool res = ::AcceptToMemoryPool(\n            txpools.getStemTxPool(),\n            state,\n            tx,\n            false,\n            NULL, /* pfMissingInputs */\n            NULL,\n            false, /* fOverrideMempoolLimit */\n            nAbsurdFee,\n            true,\n            false /* markFiroSpendTransactionSerial */\n        );\n        if (!res) {\n            LogPrintf(\n                "CMerkleTx::AcceptToMemoryPool, failed to add txn %s to dandelion stempool: %s.\\n",\n                GetHash().ToString(),\n                state.GetRejectReason());\n        }\n        return res;\n    } else {\n        // Changes to mempool should also be made to Dandelion stempool\n        return ::AcceptToMemoryPool(\n            txpools,\n            state,\n            tx,\n            false,\n            NULL, /* pfMissingInputs */\n            NULL,\n            false, /* fOverrideMempoolLimit */\n            nAbsurdFee,\n            true,\n            true);\n    }\n}\n\nbool CompSigmaHeight(const CSigmaEntry &a, const CSigmaEntry &b) { return a.nHeight < b.nHeight; }\nbool CompSigmaID(const CSigmaEntry &a, const CSigmaEntry &b) { return a.id < b.id; }\n', 'message': "Merge branch 'core14' of github.com:zcoinofficial/zcoin into core14\n", 'author_name': 'Andrey', 'lang': 'cpp'}