From 2c8f88fc67af7916670ce0086e6a9078df8182f0 Mon Sep 17 00:00:00 2001 From: alex Date: Sun, 12 Jan 2014 12:47:13 +0400 Subject: [PATCH] Pre-0.4.8 update * New stake miner implementation, almost 100 times faster; * Qt: Unification of coinbase and coinstake transactions processing; * Switch to libdb-6.0.20 for windows builds. --- src/main.cpp | 88 ++++++-------- src/main.h | 8 +- src/makefile.linux-mingw | 4 +- src/miner.cpp | 63 +++------- src/qt/transactionrecord.cpp | 24 +++-- src/qt/transactionrecord.h | 3 +- src/qt/transactiontablemodel.cpp | 10 +- src/qt/transactionview.cpp | 1 - src/rpcmining.cpp | 11 ++- src/wallet.cpp | 243 +++++++++++++++++++++++++++++--------- src/wallet.h | 14 +-- 11 files changed, 284 insertions(+), 185 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b0eddf0..c830a8f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2117,7 +2117,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c return DoS(50, error("CheckBlock() : proof of work failed")); // Check timestamp - if (GetBlockTime() > GetAdjustedTime() + nMaxClockDrift) + if (GetBlockTime() > FutureDrift(GetAdjustedTime())) return error("CheckBlock() : block timestamp too far in the future"); // First transaction must be coinbase, the rest must not be @@ -2128,7 +2128,7 @@ bool CBlock::CheckBlock(bool fCheckPOW, bool fCheckMerkleRoot, bool fCheckSig) c return DoS(100, error("CheckBlock() : more than one coinbase")); // Check coinbase timestamp - if (GetBlockTime() > (int64)vtx[0].nTime + nMaxClockDrift) + if (GetBlockTime() > FutureDrift((int64)vtx[0].nTime)) return DoS(50, error("CheckBlock() : coinbase timestamp is too early")); if (IsProofOfStake()) @@ -2233,7 +2233,7 @@ bool CBlock::AcceptBlock() return DoS(100, error("AcceptBlock() : incorrect %s", IsProofOfWork() ? "proof-of-work" : "proof-of-stake")); // Check timestamp against prev - if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || GetBlockTime() + nMaxClockDrift < pindexPrev->GetBlockTime()) + if (GetBlockTime() <= pindexPrev->GetMedianTimePast() || FutureDrift(GetBlockTime()) < pindexPrev->GetBlockTime()) return error("AcceptBlock() : block's timestamp is too early"); // Check that all transactions are finalized @@ -2503,61 +2503,53 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) return true; } -// ppcoin: sign block -bool CBlock::SignBlock(const CKeyStore& keystore) +// novacoin: attempt to generate suitable proof-of-stake +bool CBlock::SignBlock(CWallet& wallet) { - vector vSolutions; - txnouttype whichType; - - if(!IsProofOfStake()) - { - for(unsigned int i = 0; i < vtx[0].vout.size(); i++) - { - const CTxOut& txout = vtx[0].vout[i]; + // if we are trying to sign + // something except proof-of-stake block template + if (!vtx[0].vout[0].IsEmpty()) + return false; - if (!Solver(txout.scriptPubKey, whichType, vSolutions)) - continue; + // if we are trying to sign + // a complete proof-of-stake block + if (IsProofOfStake()) + return true; - if (whichType == TX_PUBKEY) - { - // Sign - valtype& vchPubKey = vSolutions[0]; - CKey key; + static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp - if (!keystore.GetKey(Hash160(vchPubKey), key)) - continue; - if (key.GetPubKey() != vchPubKey) - continue; - if(!key.Sign(GetHash(), vchBlockSig)) - continue; + CKey key; + CTransaction txCoinStake; + int64 nSearchTime = txCoinStake.nTime; // search to current time - return true; - } - } - } - else + if (nSearchTime > nLastCoinStakeSearchTime) { - const CTxOut& txout = vtx[1].vout[1]; - - if (!Solver(txout.scriptPubKey, whichType, vSolutions)) - return false; - - if (whichType == TX_PUBKEY) + if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key)) { - // Sign - valtype& vchPubKey = vSolutions[0]; - CKey key; - - if (!keystore.GetKey(Hash160(vchPubKey), key)) - return false; - if (key.GetPubKey() != vchPubKey) - return false; - - return key.Sign(GetHash(), vchBlockSig); + if (txCoinStake.nTime >= max(pindexBest->GetMedianTimePast()+1, PastDrift(pindexBest->GetBlockTime()))) + { + // make sure coinstake would meet timestamp protocol + // as it would be the same as the block timestamp + vtx[0].nTime = nTime = txCoinStake.nTime; + nTime = max(pindexBest->GetMedianTimePast()+1, GetMaxTransactionTime()); + nTime = max(GetBlockTime(), PastDrift(pindexBest->GetBlockTime())); + + // we have to make sure that we have no future timestamps in + // our transactions set + for (vector::iterator it = vtx.begin(); it != vtx.end();) + if (it->nTime > nTime) { it = vtx.erase(it); } else { ++it; } + + vtx.insert(vtx.begin() + 1, txCoinStake); + hashMerkleRoot = BuildMerkleTree(); + + // append a signature to our block + return key.Sign(GetHash(), vchBlockSig); + } } + nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; + nLastCoinStakeSearchTime = nSearchTime; } - printf("Sign failed\n"); return false; } diff --git a/src/main.h b/src/main.h index 267819b..58ec9fa 100644 --- a/src/main.h +++ b/src/main.h @@ -58,7 +58,9 @@ static const int fHaveUPnP = false; static const uint256 hashGenesisBlock("0x00000a060336cbb72fe969666d337b87198b1add2abaa59cca226820b32933a4"); static const uint256 hashGenesisBlockTestNet("0x000c763e402f2436da9ed36c7286f62c3f6e5dbafce9ff289bd43d7459327eb"); -static const int64 nMaxClockDrift = 2 * 60 * 60; // two hours + +inline int64 PastDrift(int64 nTime) { return nTime - 2 * 60 * 60; } // up to 2 hours from the past +inline int64 FutureDrift(int64 nTime) { return nTime + 2 * 60 * 60; } // up to 2 hours from the future extern libzerocoin::Params* ZCParams; extern CScript COINBASE_FLAGS; @@ -1099,7 +1101,7 @@ public: bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const; bool AcceptBlock(); bool GetCoinAge(uint64& nCoinAge) const; // ppcoin: calculate total coin age spent in block - bool SignBlock(const CKeyStore& keystore); + bool SignBlock(CWallet& keystore); bool CheckBlockSignature(bool fProofOfStake) const; private: @@ -1407,7 +1409,7 @@ public: uint256 GetBlockHash() const { - if (fUseFastIndex && (nTime < GetAdjustedTime() - 12 * nMaxClockDrift) && blockHash != 0) + if (fUseFastIndex && (nTime < GetAdjustedTime() - 24 * 60 * 60) && blockHash != 0) return blockHash; CBlock block; diff --git a/src/makefile.linux-mingw b/src/makefile.linux-mingw index 698c915..5ff4cf3 100644 --- a/src/makefile.linux-mingw +++ b/src/makefile.linux-mingw @@ -19,13 +19,13 @@ INCLUDEPATHS= \ -I"$(CURDIR)" \ -I"$(CURDIR)"/obj \ -I"$(DEPSDIR)/boost_1_55_0" \ - -I"$(DEPSDIR)/db-5.3.21/build_unix" \ + -I"$(DEPSDIR)/db-6.0.20/build_unix" \ -I"$(DEPSDIR)/openssl-1.0.1f/include" \ -I"$(DEPSDIR)" LIBPATHS= \ -L"$(DEPSDIR)/boost_1_55_0/stage/lib" \ - -L"$(DEPSDIR)/db-5.3.21/build_unix" \ + -L"$(DEPSDIR)/db-6.0.20/build_unix" \ -L"$(DEPSDIR)/openssl-1.0.1f" LIBS= \ diff --git a/src/miner.cpp b/src/miner.cpp index bdae5c7..3b54b96 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -107,8 +107,7 @@ public: } }; -// CreateNewBlock: -// fProofOfStake: try (best effort) to make a proof-of-stake block +// CreateNewBlock: create new block (without proof-of-work/proof-of-stake) CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) { // Create new block @@ -157,32 +156,9 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (mapArgs.count("-mintxfee")) ParseMoney(mapArgs["-mintxfee"], nMinTxFee); - // ppcoin: if coinstake available add coinstake tx - static int64 nLastCoinStakeSearchTime = GetAdjustedTime(); // only initialized at startup CBlockIndex* pindexPrev = pindexBest; - if (fProofOfStake) // attempt to find a coinstake - { - pblock->nBits = GetNextTargetRequired(pindexPrev, true); - CTransaction txCoinStake; - int64 nSearchTime = txCoinStake.nTime; // search to current time - if (nSearchTime > nLastCoinStakeSearchTime) - { - if (pwallet->CreateCoinStake(*pwallet, pblock->nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake)) - { - if (txCoinStake.nTime >= max(pindexPrev->GetMedianTimePast()+1, pindexPrev->GetBlockTime() - nMaxClockDrift)) - { // make sure coinstake would meet timestamp protocol - // as it would be the same as the block timestamp - pblock->vtx[0].nTime = txCoinStake.nTime; - pblock->vtx.push_back(txCoinStake); - } - } - nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; - nLastCoinStakeSearchTime = nSearchTime; - } - } - - pblock->nBits = GetNextTargetRequired(pindexPrev, pblock->IsProofOfStake()); + pblock->nBits = GetNextTargetRequired(pindexPrev, fProofOfStake); // Collect memory pool transactions into the block int64 nFees = 0; @@ -297,10 +273,10 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) continue; // Timestamp limit - if (tx.nTime > GetAdjustedTime() || (pblock->IsProofOfStake() && tx.nTime > pblock->vtx[1].nTime)) + if (tx.nTime > GetAdjustedTime() || (fProofOfStake && tx.nTime > pblock->vtx[0].nTime)) continue; - // ppcoin: simplify transaction fee - allow free = false + // Simplify transaction fee - allow free = false int64 nMinFee = tx.GetMinFee(nBlockSize, false, GMF_BLOCK); // Skip free transactions if we're past the minimum block size: @@ -376,16 +352,14 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) if (fDebug && GetBoolArg("-printpriority")) printf("CreateNewBlock(): total size %"PRI64u"\n", nBlockSize); - if (pblock->IsProofOfWork()) + if (!fProofOfStake) pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits); // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - if (pblock->IsProofOfStake()) - pblock->nTime = pblock->vtx[1].nTime; //same as coinstake timestamp pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); - pblock->nTime = max(pblock->GetBlockTime(), pindexPrev->GetBlockTime() - nMaxClockDrift); - if (pblock->IsProofOfWork()) + pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime())); + if (!fProofOfStake) pblock->UpdateTime(pindexPrev); pblock->nNonce = 0; } @@ -404,6 +378,7 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& hashPrevBlock = pblock->hashPrevBlock; } ++nExtraNonce; + unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2 pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS; assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100); @@ -548,18 +523,22 @@ void StakeMiner(CWallet *pwallet) { if (fShutdown) return; - while (vNodes.empty() || IsInitialBlockDownload()) + + while (pwallet->IsLocked()) { + strMintWarning = strMintMessage; Sleep(1000); if (fShutdown) return; } - while (pwallet->IsLocked()) + while (vNodes.empty() || IsInitialBlockDownload()) { - strMintWarning = strMintMessage; Sleep(1000); + if (fShutdown) + return; } + strMintWarning = ""; // @@ -572,16 +551,10 @@ void StakeMiner(CWallet *pwallet) return; IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); - if(pblock->IsProofOfStake()) + // Trying to sign a block + if (pblock->SignBlock(*pwallet)) { - // Trying to sign a block - if (!pblock->SignBlock(*pwallet)) - { - strMintWarning = strMintMessage; - continue; - } - - strMintWarning = ""; + strMintWarning = _("Stake generation: new block found!"); SetThreadPriority(THREAD_PRIORITY_NORMAL); CheckStake(pblock.get(), *pwallet); SetThreadPriority(THREAD_PRIORITY_LOWEST); diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp index 18858e2..03862ae 100644 --- a/src/qt/transactionrecord.cpp +++ b/src/qt/transactionrecord.cpp @@ -28,15 +28,10 @@ QList TransactionRecord::decomposeTransaction(const CWallet * int64 nCredit = wtx.GetCredit(true); int64 nDebit = wtx.GetDebit(); int64 nNet = nCredit - nDebit; - uint256 hash = wtx.GetHash(); + uint256 hash = wtx.GetHash(), hashPrev = 0; std::map mapValue = wtx.mapValue; - if (wtx.IsCoinStake()) - { - // Stake generation - parts.append(TransactionRecord(hash, nTime, TransactionRecord::StakeMint, "", -nDebit, wtx.GetValueOut())); - } - else if (nNet > 0 || wtx.IsCoinBase()) + if (nNet > 0 || wtx.IsCoinBase() || wtx.IsCoinStake()) { // // Credit @@ -63,8 +58,19 @@ QList TransactionRecord::decomposeTransaction(const CWallet * } if (wtx.IsCoinBase()) { - // Generated + // Generated (proof-of-work) + sub.type = TransactionRecord::Generated; + } + if (wtx.IsCoinStake()) + { + // Generated (proof-of-stake) + + if (hashPrev == hash) + continue; // last coinstake output + sub.type = TransactionRecord::Generated; + sub.credit = nNet > 0 ? nNet : wtx.GetValueOut() - nDebit; + hashPrev = hash; } parts.append(sub); @@ -197,7 +203,7 @@ void TransactionRecord::updateStatus(const CWalletTx &wtx) } // For generated transactions, determine maturity - if(type == TransactionRecord::Generated || type == TransactionRecord::StakeMint) + if(type == TransactionRecord::Generated) { int64 nCredit = wtx.GetCredit(true); if (nCredit == 0) diff --git a/src/qt/transactionrecord.h b/src/qt/transactionrecord.h index 8d4ab03..db06374 100644 --- a/src/qt/transactionrecord.h +++ b/src/qt/transactionrecord.h @@ -68,8 +68,7 @@ public: SendToOther, RecvWithAddress, RecvFromOther, - SendToSelf, - StakeMint + SendToSelf }; /** Number of confirmation needed for transaction */ diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp index 48c03d3..f0ec309 100644 --- a/src/qt/transactiontablemodel.cpp +++ b/src/qt/transactiontablemodel.cpp @@ -295,7 +295,8 @@ QString TransactionTableModel::formatTxStatus(const TransactionRecord *wtx) cons status = tr("Confirmed (%1 confirmations)").arg(wtx->status.depth); break; } - if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint) + + if(wtx->type == TransactionRecord::Generated) { switch(wtx->status.maturity) { @@ -359,7 +360,6 @@ QString TransactionTableModel::formatTxType(const TransactionRecord *wtx) const return tr("Sent to"); case TransactionRecord::SendToSelf: return tr("Payment to yourself"); - case TransactionRecord::StakeMint: case TransactionRecord::Generated: return tr("Mined"); default: @@ -372,7 +372,6 @@ QVariant TransactionTableModel::txAddressDecoration(const TransactionRecord *wtx switch(wtx->type) { case TransactionRecord::Generated: - case TransactionRecord::StakeMint: return QIcon(":/icons/tx_mined"); case TransactionRecord::RecvWithAddress: case TransactionRecord::RecvFromOther: @@ -440,7 +439,7 @@ QString TransactionTableModel::formatTxAmount(const TransactionRecord *wtx, bool QVariant TransactionTableModel::txStatusDecoration(const TransactionRecord *wtx) const { - if(wtx->type == TransactionRecord::Generated || wtx->type == TransactionRecord::StakeMint) + if(wtx->type == TransactionRecord::Generated) { switch(wtx->status.maturity) { @@ -575,8 +574,7 @@ QVariant TransactionTableModel::data(const QModelIndex &index, int role) const return QString::fromStdString(rec->getTxID()); case ConfirmedRole: // Return True if transaction counts for balance - return rec->status.confirmed && !((rec->type == TransactionRecord::Generated || rec->type == TransactionRecord::StakeMint) && - rec->status.maturity != TransactionStatus::Mature); + return rec->status.confirmed && !(rec->type == TransactionRecord::Generated && rec->status.maturity != TransactionStatus::Mature); case FormattedAmountRole: return formatTxAmount(rec, false); } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 629cb98..6489135 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -74,7 +74,6 @@ TransactionView::TransactionView(QWidget *parent) : typeWidget->addItem(tr("Sent to"), TransactionFilterProxy::TYPE(TransactionRecord::SendToAddress) | TransactionFilterProxy::TYPE(TransactionRecord::SendToOther)); typeWidget->addItem(tr("To yourself"), TransactionFilterProxy::TYPE(TransactionRecord::SendToSelf)); - typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::StakeMint)); typeWidget->addItem(tr("Mined"), TransactionFilterProxy::TYPE(TransactionRecord::Generated)); typeWidget->addItem(tr("Other"), TransactionFilterProxy::TYPE(TransactionRecord::Other)); diff --git a/src/rpcmining.cpp b/src/rpcmining.cpp index 5d796fa..94d3671 100644 --- a/src/rpcmining.cpp +++ b/src/rpcmining.cpp @@ -20,6 +20,9 @@ Value getmininginfo(const Array& params, bool fHelp) "getmininginfo\n" "Returns an object containing mining-related information."); + uint64 nMinWeight = 0, nMaxWeight = 0, nWeight = 0; + pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight); + Object obj; obj.push_back(Pair("blocks", (int)nBestHeight)); obj.push_back(Pair("currentblocksize",(uint64_t)nLastBlockSize)); @@ -30,9 +33,11 @@ Value getmininginfo(const Array& params, bool fHelp) obj.push_back(Pair("netstakeweight", GetPoSKernelPS())); obj.push_back(Pair("errors", GetWarnings("statusbar"))); obj.push_back(Pair("pooledtx", (uint64_t)mempool.size())); - obj.push_back(Pair("stakeweight", (uint64_t)pwalletMain->GetStakeWeight(*pwalletMain, STAKE_NORMAL))); - obj.push_back(Pair("minweight", (uint64_t)pwalletMain->GetStakeWeight(*pwalletMain, STAKE_MINWEIGHT))); - obj.push_back(Pair("maxweight", (uint64_t)pwalletMain->GetStakeWeight(*pwalletMain, STAKE_MAXWEIGHT))); + + obj.push_back(Pair("stakeweight", (uint64_t)nWeight)); + obj.push_back(Pair("minweight", (uint64_t)nMinWeight)); + obj.push_back(Pair("maxweight", (uint64_t)nMaxWeight)); + obj.push_back(Pair("stakeinterest", (uint64_t)GetProofOfStakeReward(0, GetLastBlockIndex(pindexBest, true)->nBits, GetLastBlockIndex(pindexBest, true)->nTime, true))); obj.push_back(Pair("testnet", fTestNet)); return obj; diff --git a/src/wallet.cpp b/src/wallet.cpp index 349bbdf..898ba67 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1058,6 +1058,29 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const } } +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())); + } + } +} + static void ApproximateBestSubset(vector > >vValue, int64 nTotalLower, int64 nTargetValue, vector& vfBest, int64& nBest, int iterations = 1000) { @@ -1146,8 +1169,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; @@ -1249,6 +1273,50 @@ bool CWallet::SelectCoins(int64 nTargetValue, unsigned int nSpendTime, 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)); + + 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; @@ -1389,84 +1457,91 @@ bool CWallet::CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& w } // 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; + unsigned int nTime = pcoin.first->nTime; // 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 // // Maximum TimeWeight is 90 days. - nTimeWeight = min((int64)GetTime() - nTime - nStakeMinAge, (int64)nStakeMaxAge); - + int64 nTimeWeight = min((int64)GetTime() - nTime - nStakeMinAge, (int64)nStakeMaxAge); CBigNum bnCoinDayWeight = CBigNum(pcoin.first->vout[pcoin.second].nValue) * nTimeWeight / COIN / (24 * 60 * 60); - nCoinAge += bnCoinDayWeight.getuint64(); - } + // Count input that is more than 90 days old + if (nTime + nStakeMaxAge < GetTime()) + { + nMaxWeight += bnCoinDayWeight.getuint64(); + nWeight += bnCoinDayWeight.getuint64(); + } - if (fDebug && GetBoolArg("-printcoinage")) - printf("StakeWeight bnCoinDay=%"PRI64d"\n", nCoinAge); + // Count input that is more than 30 days old and less than 90 days old + if (nTime + nStakeMinAge < GetTime() && nTime + nStakeMaxAge > GetTime()) + { + nMinWeight += bnCoinDayWeight.getuint64(); + nWeight += bnCoinDayWeight.getuint64(); + } + } - 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 // Should not be adjusted if you don't understand the consequences @@ -1476,40 +1551,76 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int 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 @@ -1547,7 +1658,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")) @@ -1556,10 +1666,27 @@ 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); @@ -1572,11 +1699,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 @@ -1604,6 +1734,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int vwtxPrev.push_back(pcoin.first); } } + // Calculate coin age reward { uint64 nCoinAge; diff --git a/src/wallet.h b/src/wallet.h index b0c557e..26c1a2a 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -36,14 +36,6 @@ enum WalletFeature FEATURE_LATEST = 60000 }; -/** Stake weight calculation mode */ -enum StakeWeightMode -{ - STAKE_NORMAL = 0, // all 30+ days old inputs - STAKE_MAXWEIGHT = 1, // only 90+ days old inputs - STAKE_MINWEIGHT = 3 // only [30-90] days old inputs -}; - /** A key pool entry */ class CKeyPool { @@ -77,6 +69,7 @@ public: class CWallet : public CCryptoKeyStore { private: + bool SelectCoinsSimple(int64 nTargetValue, unsigned int nSpendTime, int nMinConf, std::set >& setCoinsRet, int64& nValueRet) const; bool SelectCoins(int64 nTargetValue, unsigned int nSpendTime, std::set >& setCoinsRet, int64& nValueRet, const CCoinControl *coinControl=NULL) const; CWalletDB *pwalletdbEncryption; @@ -133,6 +126,7 @@ public: // check whether we are allowed to upgrade (or already support) to the named feature bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; } + void AvailableCoinsMinConf(std::vector& vCoins, int nConf) const; void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const; bool SelectCoinsMinConf(int64 nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64& nValueRet) const; // keystore implementation @@ -193,8 +187,8 @@ public: bool CreateTransaction(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, CReserveKey& reservekey, int64& nFeeRet, const CCoinControl *coinControl=NULL); bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey); - uint64 GetStakeWeight(const CKeyStore& keystore, enum StakeWeightMode mode=STAKE_NORMAL); - bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew); + bool GetStakeWeight(const CKeyStore& keystore, uint64& nMinWeight, uint64& nMaxWeight, uint64& nWeight); + bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64 nSearchInterval, CTransaction& txNew, CKey& key); std::string SendMoney(CScript scriptPubKey, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); std::string SendMoneyToDestination(const CTxDestination &address, int64 nValue, CWalletTx& wtxNew, bool fAskFee=false); -- 1.7.1