X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fwallet.cpp;h=85262f7ee1014ad16799a146dab778d792195355;hb=63071de429b3e0552ddffee72b0c993a7c4ea6a4;hp=e49c2150e6265664992eca6de199d399f69bd45d;hpb=16163ca1b076ee1e6e2c99cc67695e6e654a3293;p=novacoin.git diff --git a/src/wallet.cpp b/src/wallet.cpp index e49c215..85262f7 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -3,18 +3,19 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include "txdb.h" #include "wallet.h" #include "walletdb.h" #include "crypter.h" #include "ui_interface.h" #include "base58.h" #include "kernel.h" +#include "coincontrol.h" #include using namespace std; extern int nStakeMaxAge; - ////////////////////////////////////////////////////////////////////////////// // // mapWallet @@ -247,7 +248,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) vMasterKey.resize(WALLET_CRYPTO_KEY_SIZE); RAND_bytes(&vMasterKey[0], WALLET_CRYPTO_KEY_SIZE); - CMasterKey kMasterKey; + CMasterKey kMasterKey(nDerivationMethodIndex); RandAddSeedPerfmon(); kMasterKey.vchSalt.resize(WALLET_CRYPTO_SALT_SIZE); @@ -352,7 +353,7 @@ CWallet::TxItems CWallet::OrderedTxItems(std::list& acentries, return txOrdered; } -void CWallet::WalletUpdateSpent(const CTransaction &tx) +void CWallet::WalletUpdateSpent(const CTransaction &tx, bool fBlock) { // Anytime a signature is successfully verified, it's proof the outpoint is spent. // Update the wallet spent flag if it doesn't know due to wallet.dat being @@ -376,6 +377,24 @@ void CWallet::WalletUpdateSpent(const CTransaction &tx) } } } + + if (fBlock) + { + uint256 hash = tx.GetHash(); + map::iterator mi = mapWallet.find(hash); + CWalletTx& wtx = (*mi).second; + + BOOST_FOREACH(const CTxOut& txout, tx.vout) + { + if (IsMine(txout)) + { + wtx.MarkUnspent(&txout - &tx.vout[0]); + wtx.WriteToDisk(); + NotifyTransactionChanged(this, hash, CT_UPDATED); + } + } + } + } } @@ -498,7 +517,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn) } #endif // since AddToWallet is called directly for self-originating transactions, check for consumption of own coins - WalletUpdateSpent(wtx); + WalletUpdateSpent(wtx, (wtxIn.hashBlock != 0)); // Notify UI of new or updated transaction NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED); @@ -903,7 +922,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb) { uint256 hash = tx.GetHash(); if (!txdb.ContainsTx(hash)) - RelayMessage(CInv(MSG_TX, hash), (CTransaction)tx); + RelayTransaction((CTransaction)tx, hash); } } if (!(IsCoinBase() || IsCoinStake())) @@ -912,7 +931,7 @@ void CWalletTx::RelayWalletTransaction(CTxDB& txdb) if (!txdb.ContainsTx(hash)) { printf("Relaying wtx %s\n", hash.ToString().substr(0,10).c_str()); - RelayMessage(CInv(MSG_TX, hash), (CTransaction)*this); + RelayTransaction((CTransaction)*this, hash); } } } @@ -1025,7 +1044,7 @@ int64 CWallet::GetImmatureBalance() const } // populate vCoins with vector of spendable COutputs -void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const +void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const { vCoins.clear(); @@ -1048,7 +1067,32 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed) const continue; for (unsigned int i = 0; i < pcoin->vout.size(); i++) - if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0) + if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue && + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + + } + } +} + +void CWallet::AvailableCoinsMinConf(vector& vCoins, int nConf) const +{ + vCoins.clear(); + + { + LOCK(cs_wallet); + for (map::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + if (!pcoin->IsFinal()) + continue; + + if(pcoin->GetDepthInMainChain() < nConf) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) + if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue >= nMinimumInputValue) vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); } } @@ -1142,8 +1186,9 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in int i = output.i; + // Follow the timestamp rules if (pcoin->nTime > nSpendTime) - continue; // ppcoin: timestamp must not exceed spend time + continue; int64 n = pcoin->vout[i].nValue; @@ -1224,20 +1269,72 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, in return true; } -bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet) const +bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, set >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl) const { vector vCoins; - AvailableCoins(vCoins); + AvailableCoins(vCoins, true, coinControl); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } return (SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 6, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 1, 1, vCoins, setCoinsRet, nValueRet) || SelectCoinsMinConf(nTargetValue, nSpendTime, 0, 1, vCoins, setCoinsRet, nValueRet)); } +// Select some coins without random shuffle or best subset approximation +bool CWallet::SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int nMinConf, set >& setCoinsRet, int64& nValueRet) const +{ + vector vCoins; + AvailableCoinsMinConf(vCoins, nMinConf); + setCoinsRet.clear(); + nValueRet = 0; + BOOST_FOREACH(COutput output, vCoins) + { + const CWalletTx *pcoin = output.tx; + int i = output.i; + + // Stop if we've chosen enough inputs + if (nValueRet >= nTargetValue) + break; + + // Follow the timestamp rules + if (pcoin->nTime > nSpendTime) + continue; + + int64 n = pcoin->vout[i].nValue; + + pair > coin = make_pair(n,make_pair(pcoin, i)); -bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) + if (n >= nTargetValue) + { + // If input value is greater or equal to target then simply insert + // it into the current subset and exit + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + break; + } + else if (n < nTargetValue + CENT) + { + setCoinsRet.insert(coin.second); + nValueRet += coin.first; + } + } + + return true; +} + +bool CWallet::CreateTransaction(const vector >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl) { int64 nValue = 0; BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) @@ -1257,7 +1354,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW CTxDB txdb("r"); { nFeeRet = nTransactionFee; - loop + while (true) { wtxNew.vin.clear(); wtxNew.vout.clear(); @@ -1272,7 +1369,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Choose coins to use set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn)) + if (!SelectCoins(nTotalValue, wtxNew.nTime, setCoins, nValueIn, coinControl)) return false; BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { @@ -1300,22 +1397,30 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW if (nChange > 0) { - // Note: We use a new key here to keep it from being obvious which side is the change. - // The drawback is that by not reusing a previous key, the change may be lost if a - // backup is restored, if the backup doesn't have the new private key for the change. - // If we reused the old key, it would be possible to add code to look for and - // rediscover unknown transactions that were written with keys of ours to recover - // post-backup change. - - // Reserve a new key pair from key pool - CPubKey vchPubKey = reservekey.GetReservedKey(); - // assert(mapKeys.count(vchPubKey)); - // Fill a vout to ourself // TODO: pass in scriptChange instead of reservekey so // change transaction isn't always pay-to-bitcoin-address CScript scriptChange; - scriptChange.SetDestination(vchPubKey.GetID()); + + // coin control: send change to custom address + if (coinControl && !boost::get(&coinControl->destChange)) + scriptChange.SetDestination(coinControl->destChange); + + // no coin control: send change to newly generated address + else + { + // Note: We use a new key here to keep it from being obvious which side is the change. + // The drawback is that by not reusing a previous key, the change may be lost if a + // backup is restored, if the backup doesn't have the new private key for the change. + // If we reused the old key, it would be possible to add code to look for and + // rediscover unknown transactions that were written with keys of ours to recover + // post-backup change. + + // Reserve a new key pair from key pool + CPubKey vchPubKey = reservekey.GetReservedKey(); + + scriptChange.SetDestination(vchPubKey.GetID()); + } // Insert change txn at random position: vector::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()); @@ -1342,7 +1447,7 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW // Check that enough fee is included int64 nPayFee = nTransactionFee * (1 + (int64)nBytes / 1000); - int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND); + int64 nMinFee = wtxNew.GetMinFee(1, false, GMF_SEND, nBytes); if (nFeeRet < max(nPayFee, nMinFee)) { @@ -1361,143 +1466,174 @@ bool CWallet::CreateTransaction(const vector >& vecSend, CW return true; } -bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet) +bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl* coinControl) { vector< pair > vecSend; vecSend.push_back(make_pair(scriptPubKey, nValue)); - return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet); + return CreateTransaction(vecSend, wtxNew, reservekey, nFeeRet, coinControl); } // NovaCoin: get current stake weight -uint64 CWallet::GetStakeWeight(const CKeyStore& keystore, enum StakeWeightMode mode) +bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight) { - LOCK2(cs_main, cs_wallet); - // Choose coins to use int64 nBalance = GetBalance(); int64 nReserveBalance = 0; - uint64 nCoinAge = 0; if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) { error("GetStakeWeight : invalid reserve balance amount"); - return 0; + return false; } if (nBalance <= nReserveBalance) - return 0; + return false; - set > setCoins; vector vwtxPrev; + +/* + * TODO: performance comparison + + static set > setCoins; + static uint256 hashPrevBlock; + static int64 nValueIn = 0; + + // Cache outputs unless best block changed + if (hashPrevBlock != pindexBest->GetBlockHash()) + { + if (!SelectCoinsSimple(nBalance - nReserveBalance, GetAdjustedTime(), nCoinbaseMaturity * 10, setCoins, nValueIn)) + return false; + + if (setCoins.empty()) + return false; + + hashPrevBlock == pindexBest->GetBlockHash(); + } +*/ + + set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(nBalance - nReserveBalance, GetTime(), setCoins, nValueIn)) - return 0; + + if (!SelectCoinsSimple(nBalance - nReserveBalance, GetTime(), nCoinbaseMaturity * 10, setCoins, nValueIn)) + return false; + if (setCoins.empty()) - return 0; + return false; + CTxDB txdb("r"); BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { - CTxDB txdb("r"); CTxIndex txindex; - if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) - continue; - - unsigned int nTime = pcoin.first->nTime; - - switch(mode) { - case STAKE_NORMAL: - // Do not count input that is still less than 30 days old - if (nTime + nStakeMinAge > GetTime()) - continue; - break; - case STAKE_MAXWEIGHT: - // Do not count input that is still less than 90 days old - if (nTime + nStakeMaxAge > GetTime()) - continue; - break; - case STAKE_MINWEIGHT: - // Count only inputs with suitable age (from 30 to 90 days old) - if (nTime + nStakeMaxAge < GetTime()) - continue; - if (nTime + nStakeMinAge > GetTime()) - continue; - break; + LOCK2(cs_main, cs_wallet); + if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) + continue; } - int64 nTimeWeight; + int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)GetTime()); + CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60); - // Kernel hash weight starts from 0 at the 30-day min age - // this change increases active coins participating the hash and helps - // to secure the network when proof-of-stake difficulty is low - // - if(fTestNet || (STAKEWEIGHT_SWITCH_TIME < nTime)) + // Weight is greater than zero + if (nTimeWeight > 0) { - // New rule since 01 Jan 2014: Maximum TimeWeight is 90 days. - nTimeWeight = min((int64)GetTime() - nTime - nStakeMinAge, (int64)nStakeMaxAge); + nWeight += bnCoinDayWeight.getuint64(); } - else + + // Weight is greater than zero, but the maximum value isn't reached yet + if (nTimeWeight > 0 && nTimeWeight < nStakeMaxAge) { - // Current rule: Maximum TimeWeight is 60 days. - nTimeWeight = min((int64)GetTime() - nTime, (int64)nStakeMaxAge) - nStakeMinAge; + nMinWeight += bnCoinDayWeight.getuint64(); } - CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60); - - nCoinAge += bnCoinDayWeight.getuint64(); + // Maximum weight was reached + if (nTimeWeight == nStakeMaxAge) + { + nMaxWeight += bnCoinDayWeight.getuint64(); + } } - if (fDebug && GetBoolArg("-printcoinage")) - printf("StakeWeight bnCoinDay=%"PRI64d"\n", nCoinAge); - - return nCoinAge; + return true; } -// ppcoin: create coin stake transaction -bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew) +bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew, CKey& key) { - // The following split & combine thresholds are important to security + // The following combine threshold is important to security // Should not be adjusted if you don't understand the consequences - static unsigned int nStakeSplitAge = (60 * 60 * 24 * 90); int64 nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; CBigNum bnTargetPerCoinDay; bnTargetPerCoinDay.SetCompact(nBits); - LOCK2(cs_main, cs_wallet); txNew.vin.clear(); txNew.vout.clear(); + // Mark coin stake transaction CScript scriptEmpty; scriptEmpty.clear(); txNew.vout.push_back(CTxOut(0, scriptEmpty)); + // Choose coins to use int64 nBalance = GetBalance(); int64 nReserveBalance = 0; + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) return error("CreateCoinStake : invalid reserve balance amount"); + if (nBalance <= nReserveBalance) return false; - set > setCoins; + vector vwtxPrev; + +/* + * TODO: performance comparison + + static set > setCoins; + static uint256 hashPrevBlock; + static int64 nValueIn = 0; + + // Cache outputs unless best block changed + if (hashPrevBlock != pindexBest->GetBlockHash()) + { + if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn)) + return false; + + if (setCoins.empty()) + return false; + + hashPrevBlock == pindexBest->GetBlockHash(); + } +*/ + + set > setCoins; int64 nValueIn = 0; - if (!SelectCoins(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn)) + + // Select coins with suitable depth + if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn)) return false; + if (setCoins.empty()) return false; + int64 nCredit = 0; CScript scriptPubKeyKernel; + CTxDB txdb("r"); BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { - CTxDB txdb("r"); CTxIndex txindex; - if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) - continue; + { + LOCK2(cs_main, cs_wallet); + if (!txdb.ReadTxIndex(pcoin.first->GetHash(), txindex)) + continue; + } // Read block header CBlock block; - if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - continue; + { + LOCK2(cs_main, cs_wallet); + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + continue; + } + static int nMaxStakeSearchInterval = 60; if (block.GetBlockTime() + nStakeMinAge > txNew.nTime - nMaxStakeSearchInterval) continue; // only count coins meeting min age requirement @@ -1507,9 +1643,9 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int { // Search backward in time from the given txNew timestamp // Search nSearchInterval seconds back up to nMaxStakeSearchInterval - uint256 hashProofOfStake = 0; + uint256 hashProofOfStake = 0, targetProofOfStake = 0; COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); - if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake)) + if (CheckStakeKernelHash(nBits, block, txindex.pos.nTxPos - txindex.pos.nBlockPos, *pcoin.first, prevoutStake, txNew.nTime - n, hashProofOfStake, targetProofOfStake)) { // Found a kernel if (fDebug && GetBoolArg("-printcoinstake")) @@ -1535,7 +1671,6 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (whichType == TX_PUBKEYHASH) // pay to address type { // convert to pay to public key type - CKey key; if (!keystore.GetKey(uint160(vSolutions[0]), key)) { if (fDebug && GetBoolArg("-printcoinstake")) @@ -1544,15 +1679,33 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int } scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; } - else + if (whichType == TX_PUBKEY) + { + valtype& vchPubKey = vSolutions[0]; + if (!keystore.GetKey(Hash160(vchPubKey), key)) + { + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + break; // unable to find corresponding public key + } + + if (key.GetPubKey() != vchPubKey) + { + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : invalid key for kernel type=%d\n", whichType); + break; // keys mismatch + } + scriptPubKeyOut = scriptPubKeyKernel; + } - txNew.nTime -= n; + txNew.nTime -= n; txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; vwtxPrev.push_back(pcoin.first); txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); - if (block.GetBlockTime() + nStakeSplitAge > txNew.nTime) + + if (GetWeight(block.GetBlockTime(), (int64)txNew.nTime) < nStakeMaxAge) txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake if (fDebug && GetBoolArg("-printcoinstake")) printf("CreateCoinStake : added kernel type=%d\n", whichType); @@ -1560,11 +1713,14 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int break; } } + if (fKernelFound || fShutdown) break; // if kernel is found stop searching } + if (nCredit == 0 || nCredit > nBalance - nReserveBalance) return false; + BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { // Attempt to add more inputs @@ -1572,6 +1728,8 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (txNew.vout.size() == 2 && ((pcoin.first->vout[pcoin.second].scriptPubKey == scriptPubKeyKernel || pcoin.first->vout[pcoin.second].scriptPubKey == txNew.vout[1].scriptPubKey)) && pcoin.first->GetHash() != txNew.vin[0].prevout.hash) { + int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)txNew.nTime); + // Stop adding more inputs if already too many inputs if (txNew.vin.size() >= 100) break; @@ -1585,13 +1743,15 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold) continue; // Do not add input that is still too young - if (pcoin.first->nTime + nStakeMaxAge > txNew.nTime) + if (nTimeWeight < nStakeMaxAge) continue; + txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); nCredit += pcoin.first->vout[pcoin.second].nValue; vwtxPrev.push_back(pcoin.first); } } + // Calculate coin age reward { uint64 nCoinAge; @@ -1602,7 +1762,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int } int64 nMinFee = 0; - loop + while (true) { // Set output amount if (txNew.vout.size() == 3)