#include "coincontrol.h"
#include <boost/algorithm/string/replace.hpp>
+#include "main.h"
+
using namespace std;
-extern int nStakeMaxAge;
+
bool fCoinsDataActual;
wtx.MarkSpent(txin.prevout.n);
wtx.WriteToDisk();
NotifyTransactionChanged(this, txin.prevout.hash, CT_UPDATED);
+ vMintingWalletUpdated.push_back(txin.prevout.hash);
}
}
}
wtx.MarkUnspent(&txout - &tx.vout[0]);
wtx.WriteToDisk();
NotifyTransactionChanged(this, hash, CT_UPDATED);
+ vMintingWalletUpdated.push_back(hash);
}
}
}
// Notify UI of new or updated transaction
NotifyTransactionChanged(this, hash, fInsertedNew ? CT_NEW : CT_UPDATED);
-
+ vMintingWalletUpdated.push_back(hash);
// notify an external script when a wallet transaction comes in or is updated
std::string strCmd = GetArg("-walletnotify", "");
int64 CWalletTx::GetTxTime() const
{
- int64 n = nTimeSmart;
- return n ? n : nTimeReceived;
+ return nTime;
}
int CWalletTx::GetRequestCount() const
}
}
-void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf) const
+void CWallet::AvailableCoinsMinConf(vector<COutput>& vCoins, int nConf, int64 nMinValue, int64 nMaxValue) const
{
vCoins.clear();
for (unsigned int i = 0; i < pcoin->vout.size(); i++) {
isminetype mine = IsMine(pcoin->vout[i]);
- if (!(pcoin->IsSpent(i)) && mine != MINE_NO && pcoin->vout[i].nValue >= nMinimumInputValue)
+ // ignore coin if it was already spent or we don't own it
+ if (pcoin->IsSpent(i) || mine == MINE_NO)
+ continue;
+
+ // if coin value is between required limits then add new item to vector
+ if (pcoin->vout[i].nValue >= nMinValue && pcoin->vout[i].nValue < nMaxValue)
vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain(), mine == MINE_SPENDABLE));
}
}
}
// Select some coins without random shuffle or best subset approximation
-bool CWallet::SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int nMinConf, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
+bool CWallet::SelectCoinsSimple(int64 nTargetValue, int64 nMinValue, int64 nMaxValue, unsigned int nSpendTime, int nMinConf, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
{
vector<COutput> vCoins;
- AvailableCoinsMinConf(vCoins, nMinConf);
+ AvailableCoinsMinConf(vCoins, nMinConf, nMinValue, nMaxValue);
setCoinsRet.clear();
nValueRet = 0;
}
-// NovaCoin: get current stake weight
-bool CWallet::GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight)
+// NovaCoin: get current stake miner statistics
+void CWallet::GetStakeStats(float &nKernelsRate, float &nCoinDaysRate)
{
- // Choose coins to use
- int64 nBalance = GetBalance();
- int64 nReserveBalance = 0;
+ static uint64 nLastKernels = 0, nLastCoinDays = 0;
+ static float nLastKernelsRate = 0, nLastCoinDaysRate = 0;
+ static int64 nLastTime = GetTime();
- if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance))
+ if (nKernelsTried < nLastKernels)
{
- error("GetStakeWeight : invalid reserve balance amount");
- return false;
- }
-
- if (nBalance <= nReserveBalance)
- return false;
+ nLastKernels = 0;
+ nLastCoinDays = 0;
- vector<const CWalletTx*> vwtxPrev;
-
- static set<pair<const CWalletTx*,unsigned int> > setCoins;
- static uint256 hashPrevBlock;
+ nLastTime = GetTime();
+ }
- // Cache outputs unless best block or wallet transaction set changed
- if (!fCoinsDataActual)
+ int64 nInterval = GetTime() - nLastTime;
+ //if (nKernelsTried > 1000 && nInterval > 5)
+ if (nInterval > 10)
{
- int64 nValueIn = 0;
- if (!SelectCoinsSimple(nBalance - nReserveBalance, GetAdjustedTime(), nCoinbaseMaturity * 10, setCoins, nValueIn))
- return false;
+ nKernelsRate = nLastKernelsRate = ( nKernelsTried - nLastKernels ) / (float) nInterval;
+ nCoinDaysRate = nLastCoinDaysRate = ( nCoinDaysTried - nLastCoinDays ) / (float) nInterval;
- if (setCoins.empty())
- return false;
-
- fCoinsDataActual = true;
+ nLastKernels = nKernelsTried;
+ nLastCoinDays = nCoinDaysTried;
+ nLastTime = GetTime();
}
-
- CTxDB txdb("r");
- BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins)
+ else
{
- int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)GetTime());
- CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60);
-
- // Weight is greater than zero
- if (nTimeWeight > 0)
- {
- nWeight += bnCoinDayWeight.getuint64();
- }
-
- // Weight is greater than zero, but the maximum value isn't reached yet
- if (nTimeWeight > 0 && nTimeWeight < nStakeMaxAge)
- {
- nMinWeight += bnCoinDayWeight.getuint64();
- }
-
- // Maximum weight was reached
- if (nTimeWeight == nStakeMaxAge)
- {
- nMaxWeight += bnCoinDayWeight.getuint64();
- }
+ nKernelsRate = nLastKernelsRate;
+ nCoinDaysRate = nLastCoinDaysRate;
}
-
- return true;
}
-bool CWallet::MergeCoins(const int64& nAmount, const int64& nMaxValue, const int64& nOutputValue, list<uint256>& listMerged)
+bool CWallet::MergeCoins(const int64& nAmount, const int64& nMinValue, const int64& nOutputValue, list<uint256>& listMerged)
{
int64 nBalance = GetBalance();
set<pair<const CWalletTx*,unsigned int> > setCoins;
// Simple coins selection - no randomization
- if (!SelectCoinsSimple(nAmount, GetTime(), 1, setCoins, nValueIn))
+ if (!SelectCoinsSimple(nAmount, nMinValue, nOutputValue, GetTime(), 1, setCoins, nValueIn))
return false;
if (setCoins.empty())
{
int64 nCredit = pcoin.first->vout[pcoin.second].nValue;
- // Ignore coin if credit is too high
- if (nCredit >= nMaxValue)
- continue;
-
// Add current coin to inputs list and add its credit to transaction output
wtxNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
wtxNew.vout[0].nValue += nCredit;
vector<const CWalletTx*> vwtxPrev;
- static CoinsSet setCoins;
- static MetaMap mapMeta;
-
CTxDB txdb("r");
{
LOCK2(cs_main, cs_wallet);
// Cache outputs unless best block or wallet transaction set changed
if (!fCoinsDataActual)
{
- setCoins.clear();
mapMeta.clear();
-
int64 nValueIn = 0;
- if (!SelectCoinsSimple(nBalance - nReserveBalance, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
+ CoinsSet setCoins;
+ if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn))
return false;
if (setCoins.empty())
continue;
uint64 nStakeModifier = 0;
-
if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier))
- return false;
+ continue;
// Add meta record
- mapMeta[pcoin->first->GetHash()] = make_pair(txindex, make_pair(block, nStakeModifier));
+ // txid => ((txindex, (tx, vout.n)), (block, modifier))
+ mapMeta[make_pair(pcoin->first->GetHash(), pcoin->second)] = make_pair(make_pair(txindex, *pcoin), make_pair(block, nStakeModifier));
if (fDebug)
printf("Load coin: %s\n", pcoin->first->GetHash().GetHex().c_str());
}
if (fDebug)
- printf("Stake miner: %zu meta items loaded for %zu coins\n", mapMeta.size(), setCoins.size());
+ printf("Stake miner: %"PRIszu" meta items loaded for %"PRIszu" coins\n", mapMeta.size(), setCoins.size());
fCoinsDataActual = true;
+ nKernelsTried = 0;
+ nCoinDaysTried = 0;
}
}
settings.nBits = nBits;
settings.nTime = txNew.nTime;
settings.nOffset = 0;
- settings.nLimit = setCoins.size();
+ settings.nLimit = mapMeta.size();
settings.nSearchInterval = nSearchInterval;
unsigned int nTimeTx, nBlockTime;
COutPoint prevoutStake;
CoinsSet::value_type kernelcoin;
- if (ScanForStakeKernelHash(setCoins, mapMeta, settings, kernelcoin, nTimeTx, nBlockTime))
+ if (ScanForStakeKernelHash(mapMeta, settings, kernelcoin, nTimeTx, nBlockTime, nKernelsTried, nCoinDaysTried))
{
// Found a kernel
if (fDebug && GetBoolArg("-printcoinstake"))
if (nCredit == 0 || nCredit > nBalance - nReserveBalance)
return false;
- for(CoinsSet::const_iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++)
+ // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier))
+ for(MetaMap::const_iterator meta_item = mapMeta.begin(); meta_item != mapMeta.end(); meta_item++)
{
+ // Get coin
+ CoinsSet::value_type pcoin = meta_item->second.first.second;
+
// Attempt to add more inputs
// Only add coins of the same key/address as kernel
- 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)
+ 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);
+ int64 nTimeWeight = GetWeight((int64)pcoin.first->nTime, (int64)txNew.nTime);
// Stop adding more inputs if already too many inputs
if (txNew.vin.size() >= 100)
if (nCredit > nCombineThreshold)
break;
// Stop adding inputs if reached reserve limit
- if (nCredit + pcoin->first->vout[pcoin->second].nValue > nBalance - nReserveBalance)
+ if (nCredit + pcoin.first->vout[pcoin.second].nValue > nBalance - nReserveBalance)
break;
// Do not add additional significant input
- if (pcoin->first->vout[pcoin->second].nValue > nCombineThreshold)
+ if (pcoin.first->vout[pcoin.second].nValue > nCombineThreshold)
continue;
// Do not add input that is still too young
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);
+ txNew.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second));
+ nCredit += pcoin.first->vout[pcoin.second].nValue;
+ vwtxPrev.push_back(pcoin.first);
}
}
CTxDB txdb("r");
if (!txNew.GetCoinAge(txdb, nCoinAge))
return error("CreateCoinStake : failed to calculate coin age");
- nCredit += GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime);
+
+ int64 nReward = GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime);
+ // Refuse to create mint that has zero or negative reward
+ if(nReward <= 0)
+ return false;
+
+ nCredit += nReward;
}
int64 nMinFee = 0;
coin.MarkSpent(txin.prevout.n);
coin.WriteToDisk();
NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED);
+ vMintingWalletUpdated.push_back(coin.GetHash());
}
if (fFileBacked)
// Only notify UI if this transaction is in this wallet
map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(hashTx);
if (mi != mapWallet.end())
+ {
NotifyTransactionChanged(this, hashTx, CT_UPDATED);
+ vMintingWalletUpdated.push_back(hashTx);
+ }
}
}
for (std::map<CKeyID, CBlockIndex*>::const_iterator it = mapKeyFirstBlock.begin(); it != mapKeyFirstBlock.end(); it++)
mapKeyBirth[it->first] = it->second->nTime - 7200; // block times can be 2h off
}
+
+void CWallet::ClearOrphans()
+{
+ list<uint256> orphans;
+
+ LOCK(cs_wallet);
+ for(map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
+ {
+ const CWalletTx *wtx = &(*it).second;
+ if((wtx->IsCoinBase() || wtx->IsCoinStake()) && !wtx->IsInMainChain())
+ {
+ orphans.push_back(wtx->GetHash());
+ }
+ }
+
+ for(list<uint256>::const_iterator it = orphans.begin(); it != orphans.end(); ++it)
+ EraseFromWallet(*it);
+}