From 126e51d5d96f8bc2c900df18af5827c279967dcc Mon Sep 17 00:00:00 2001 From: MASM fan Date: Mon, 13 Apr 2015 14:02:11 -0700 Subject: [PATCH] New stake miner --- src/init.cpp | 4 +- src/kernel.cpp | 142 ++++++++++----------------- src/kernel.h | 20 +++- src/main.cpp | 59 +----------- src/main.h | 1 - src/miner.cpp | 248 +++++++++++++++++++++++++++++++++++++++++------ src/miner.h | 4 +- src/rpcwallet.cpp | 2 +- src/wallet.cpp | 279 +++++++++++++++++++--------------------------------- src/wallet.h | 16 +-- 10 files changed, 395 insertions(+), 380 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index b03e02f..2437e3b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -31,7 +31,6 @@ CClientUIInterface uiInterface; std::string strWalletFileName; bool fConfChange; unsigned int nNodeLifespan; -unsigned int nMinerSleep; bool fUseFastIndex; bool fUseMemoryLog; enum Checkpoints::CPMode CheckpointsMode; @@ -39,6 +38,7 @@ enum Checkpoints::CPMode CheckpointsMode; // Ping and address broadcast intervals extern int64_t nPingInterval; extern int64_t nBroadcastInterval; +extern int64_t nReserveBalance; ////////////////////////////////////////////////////////////////////////////// // @@ -377,7 +377,6 @@ bool AppInit2() nNodeLifespan = (unsigned int)(GetArg("-addrlifespan", 7)); fUseFastIndex = GetBoolArg("-fastindex", true); fUseMemoryLog = GetBoolArg("-memorylog", true); - nMinerSleep = (unsigned int)(GetArg("-minersleep", 500)); // Ping and address broadcast intervals nPingInterval = max(10 * 60, GetArg("-keepalive", 30 * 60)); @@ -738,7 +737,6 @@ bool AppInit2() if (mapArgs.count("-reservebalance")) // ppcoin: reserve balance amount { - int64_t nReserveBalance = 0; if (!ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) { InitError(_("Invalid amount for -reservebalance=")); diff --git a/src/kernel.cpp b/src/kernel.cpp index 12b26aa..4e653a4 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -94,18 +94,6 @@ bool IsFixedModifierInterval(unsigned int nTimeBlock) return (nTimeBlock >= (fTestNet? nModifierTestSwitchTime : nModifierSwitchTime)); } -// Get time weight -int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) -{ - // 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. - - return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); -} - // Get the last stake modifier and its generation time from a given block static bool GetLastStakeModifier(const CBlockIndex* pindex, uint64_t& nStakeModifier, int64_t& nModifierTime) { @@ -410,10 +398,10 @@ bool CheckStakeKernelHash(uint32_t nBits, const CBlock& blockFrom, uint32_t nTxP DateTimeStrFormat(nStakeModifierTime).c_str(), mapBlockIndex[hashBlockFrom]->nHeight, DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); - printf("CheckStakeKernelHash() : check modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", + printf("CheckStakeKernelHash() : check modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashTarget=%s hashProof=%s\n", nStakeModifier, nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, - hashProofOfStake.ToString().c_str()); + targetProofOfStake.ToString().c_str(), hashProofOfStake.ToString().c_str()); } // Now check if proof-of-stake hash meets target protocol @@ -426,89 +414,14 @@ bool CheckStakeKernelHash(uint32_t nBits, const CBlock& blockFrom, uint32_t nTxP DateTimeStrFormat(nStakeModifierTime).c_str(), mapBlockIndex[hashBlockFrom]->nHeight, DateTimeStrFormat(blockFrom.GetBlockTime()).c_str()); - printf("CheckStakeKernelHash() : pass modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashProof=%s\n", + printf("CheckStakeKernelHash() : pass modifier=0x%016" PRIx64 " nTimeBlockFrom=%u nTxPrevOffset=%u nTimeTxPrev=%u nPrevout=%u nTimeTx=%u hashTarget=%s hashProof=%s\n", nStakeModifier, nTimeBlockFrom, nTxPrevOffset, txPrev.nTime, prevout.n, nTimeTx, - hashProofOfStake.ToString().c_str()); + targetProofOfStake.ToString().c_str(), hashProofOfStake.ToString().c_str()); } return true; } -// Scan given coins set for kernel solution -bool ScanForStakeKernelHash(MetaMap &mapMeta, uint32_t nBits, uint32_t nTime, uint32_t nSearchInterval, CoinsSet::value_type &kernelcoin, uint32_t &nTimeTx, uint32_t &nBlockTime, uint64_t &nKernelsTried, uint64_t &nCoinDaysTried) -{ - uint256 hashProofOfStake = 0; - - // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier)) - for(MetaMap::const_iterator meta_item = mapMeta.begin(); meta_item != mapMeta.end(); meta_item++) - { - if (!fCoinsDataActual) - break; - - CTxIndex txindex = (*meta_item).second.first.first; - CBlock block = (*meta_item).second.second.first; - uint64_t nStakeModifier = (*meta_item).second.second.second; - - // Get coin - CoinsSet::value_type pcoin = meta_item->second.first.second; - - static unsigned int nMaxStakeSearchInterval = 60; - - // only count coins meeting min age requirement - if (nStakeMinAge + block.nTime > nTime - nMaxStakeSearchInterval) - continue; - - // Transaction offset inside block - uint32_t nTxOffset = txindex.pos.nTxPos - txindex.pos.nBlockPos; - - // Current timestamp scanning interval - unsigned int nCurrentSearchInterval = min(nSearchInterval, nMaxStakeSearchInterval); - - nBlockTime = block.nTime; - CBigNum bnTargetPerCoinDay; - bnTargetPerCoinDay.SetCompact(nBits); - int64_t nValueIn = pcoin.first->vout[pcoin.second].nValue; - - // Search backward in time from the given timestamp - // Search nSearchInterval seconds back up to nMaxStakeSearchInterval - // Stopping search in case of shutting down or cache invalidation - for (unsigned int n=0; nnTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); - CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; - - // Build kernel - CDataStream ss(SER_GETHASH, 0); - ss << nStakeModifier; - ss << nBlockTime << nTxOffset << pcoin.first->nTime << pcoin.second << nTimeTx; - - // Calculate kernel hash - hashProofOfStake = Hash(ss.begin(), ss.end()); - - // Update statistics - nKernelsTried += 1; - nCoinDaysTried += bnCoinDayWeight.getuint64(); - - if (bnTargetProofOfStake >= CBigNum(hashProofOfStake)) - { - if (fDebug) - printf("nStakeModifier=0x%016" PRIx64 ", nBlockTime=%u nTxOffset=%u nTxPrevTime=%u nVout=%u nTimeTx=%u hashProofOfStake=%s Success=true\n", - nStakeModifier, nBlockTime, nTxOffset, pcoin.first->nTime, pcoin.second, nTimeTx, hashProofOfStake.GetHex().c_str()); - - kernelcoin = pcoin; - return true; - } - - if (fDebug) - printf("nStakeModifier=0x%016" PRIx64 ", nBlockTime=%u nTxOffset=%u nTxPrevTime=%u nTxNumber=%u nTimeTx=%u hashProofOfStake=%s Success=false\n", - nStakeModifier, nBlockTime, nTxOffset, pcoin.first->nTime, pcoin.second, nTimeTx, hashProofOfStake.GetHex().c_str()); - } - } - - return false; -} - // Precompute hashing state for static part of kernel void GetKernelMidstate(uint64_t nStakeModifier, uint32_t nBlockTime, uint32_t nTxOffset, uint32_t nInputTxTime, uint32_t nOut, SHA256_CTX &ctx) { @@ -571,6 +484,53 @@ bool ScanMidstateForward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, return false; } +// Scan given midstate for solution +bool ScanMidstateBackward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution) +{ + CBigNum bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + // Get maximum possible target to filter out the majority of obviously insufficient hashes + CBigNum bnMaxTargetPerCoinDay = bnTargetPerCoinDay * CBigNum(nValueIn) * nStakeMaxAge / COIN / (24 * 60 * 60); + uint256 maxTarget = bnMaxTargetPerCoinDay.getuint256(); + + SHA256_CTX ctxCopy = ctx; + + // Search backward in time from the given timestamp + // Stopping search in case of shutting down + for (uint32_t nTimeTx=SearchInterval.first; nTimeTx>SearchInterval.second && !fShutdown; nTimeTx--) + { + // Complete first hashing iteration + uint256 hash1; + SHA256_Update(&ctxCopy, (unsigned char*)&nTimeTx, 4); + SHA256_Final((unsigned char*)&hash1, &ctxCopy); + + // Restore context + ctxCopy = ctx; + + // Finally, calculate kernel hash + uint256 hashProofOfStake; + SHA256((unsigned char*)&hash1, sizeof(hashProofOfStake), (unsigned char*)&hashProofOfStake); + + // Skip if hash doesn't satisfy the maximum target + if (hashProofOfStake > maxTarget) + continue; + + CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)nInputTxTime, (int64_t)nTimeTx) / COIN / (24 * 60 * 60); + CBigNum bnTargetProofOfStake = bnCoinDayWeight * bnTargetPerCoinDay; + + if (bnTargetProofOfStake >= CBigNum(hashProofOfStake)) + { + solution.first = hashProofOfStake; + solution.second = nTimeTx; + + return true; + } + } + + return false; +} + // Check kernel hash target and coinstake signature bool CheckProofOfStake(const CTransaction& tx, unsigned int nBits, uint256& hashProofOfStake, uint256& targetProofOfStake) { diff --git a/src/kernel.h b/src/kernel.h index a94ace2..eca9744 100644 --- a/src/kernel.h +++ b/src/kernel.h @@ -7,14 +7,14 @@ #include "main.h" #include "wallet.h" +using namespace std; + // ChainDB upgrade time extern unsigned int nModifierUpgradeTime; // MODIFIER_INTERVAL: time to elapse before new modifier is computed extern unsigned int nModifierInterval; -extern bool fCoinsDataActual; - // MODIFIER_INTERVAL_RATIO: // ratio of group interval length between the last group and the first group static const int MODIFIER_INTERVAL_RATIO = 3; @@ -33,14 +33,12 @@ bool GetKernelStakeModifier(uint256 hashBlockFrom, uint64_t& nStakeModifier); // Sets hashProofOfStake on success return bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, uint32_t nTxPrevOffset, const CTransaction& txPrev, const COutPoint& prevout, uint32_t nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake=false); -// Scan given coins set for kernel solution -bool ScanForStakeKernelHash(MetaMap &mapMeta, uint32_t nBits, uint32_t nTime, uint32_t nSearchInterval, CoinsSet::value_type &kernelcoin, uint32_t &nTimeTx, uint32_t &nBlockTime, uint64_t &nKernelsTried, uint64_t &nCoinDaysTried); - // Precompute hashing state for static part of kernel void GetKernelMidstate(uint64_t nStakeModifier, uint32_t nBlockTime, uint32_t nTxOffset, uint32_t nInputTxTime, uint32_t nOut, SHA256_CTX &ctx); // Scan given midstate for kernel solutions bool ScanMidstateForward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution); +bool ScanMidstateBackward(SHA256_CTX &ctx, uint32_t nBits, uint32_t nInputTxTime, int64_t nValueIn, std::pair &SearchInterval, std::pair &solution); // Check kernel hash target and coinstake signature // Sets hashProofOfStake on success return @@ -53,6 +51,16 @@ uint32_t GetStakeModifierChecksum(const CBlockIndex* pindex); bool CheckStakeModifierCheckpoints(int nHeight, uint32_t nStakeModifierChecksum); // Get time weight using supplied timestamps -int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd); +inline int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd) +{ + // 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. + + return min(nIntervalEnd - nIntervalBeginning - nStakeMinAge, (int64_t)nStakeMaxAge); +} + #endif // PPCOIN_KERNEL_H diff --git a/src/main.cpp b/src/main.cpp index 28bdb47..a62d641 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -151,8 +151,6 @@ void SyncWithWallets(const CTransaction& tx, const CBlock* pblock, bool fUpdate, BOOST_FOREACH(CWallet* pwallet, setpwalletRegistered) pwallet->AddToWalletIfInvolvingMe(tx, pblock, fUpdate); - // Preloaded coins cache invalidation - fCoinsDataActual = false; } // notify wallets about a new best chain @@ -1036,13 +1034,13 @@ int64_t GetProofOfWorkReward(unsigned int nBits, int64_t nFees) // Human readable form: // // nSubsidy = 100 / (diff ^ 1/6) + // + // Please note that we're using bisection to find an approximate solutuion CBigNum bnLowerBound = CENT; CBigNum bnUpperBound = bnSubsidyLimit; while (bnLowerBound + CENT <= bnUpperBound) { CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; - if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfWorkReward() : lower=%" PRId64 " upper=%" PRId64 " mid=%" PRId64 "\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); if (bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnMidValue * bnTargetLimit > bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnSubsidyLimit * bnTarget) bnUpperBound = bnMidValue; else @@ -1082,9 +1080,6 @@ int64_t GetProofOfStakeReward(int64_t nCoinAge, unsigned int nBits, int64_t nTim while (bnLowerBound + CENT <= bnUpperBound) { CBigNum bnMidValue = (bnLowerBound + bnUpperBound) / 2; - if (fDebug && GetBoolArg("-printcreation")) - printf("GetProofOfStakeReward() : lower=%" PRId64 " upper=%" PRId64 " mid=%" PRId64 "\n", bnLowerBound.getuint64(), bnUpperBound.getuint64(), bnMidValue.getuint64()); - if(!fTestNet && nTime < STAKECURVE_SWITCH_TIME) { // @@ -2627,56 +2622,6 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock) return true; } -// novacoin: attempt to generate suitable proof-of-stake -bool CBlock::SignBlock(CWallet& wallet) -{ - // if we are trying to sign - // something except proof-of-stake block template - if (!vtx[0].vout[0].IsEmpty()) - return false; - - // if we are trying to sign - // a complete proof-of-stake block - if (IsProofOfStake()) - return true; - - static uint32_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp - - CKey key; - CTransaction txCoinStake; - uint32_t nSearchTime = txCoinStake.nTime; // search to current time - - if (nSearchTime > nLastCoinStakeSearchTime) - { - if (wallet.CreateCoinStake(wallet, nBits, nSearchTime-nLastCoinStakeSearchTime, txCoinStake, key)) - { - 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; - } - - return false; -} - // ppcoin: check block signature bool CBlock::CheckBlockSignature() const { diff --git a/src/main.h b/src/main.h index c6e6558..d2df098 100644 --- a/src/main.h +++ b/src/main.h @@ -1145,7 +1145,6 @@ public: bool CheckBlock(bool fCheckPOW=true, bool fCheckMerkleRoot=true, bool fCheckSig=true) const; bool AcceptBlock(); bool GetCoinAge(uint64_t& nCoinAge) const; // ppcoin: calculate total coin age spent in block - bool SignBlock(CWallet& keystore); bool CheckBlockSignature() const; private: diff --git a/src/miner.cpp b/src/miner.cpp index aca0674..4f0130a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -15,7 +15,8 @@ using namespace std; // BitcoinMiner // -extern unsigned int nMinerSleep; +int64_t nReserveBalance = 0; +static unsigned int nMaxStakeSearchInterval = 60; int static FormatHashBlocks(void* pbuffer, unsigned int len) { @@ -106,30 +107,42 @@ public: } }; -// CreateNewBlock: create new block (without proof-of-work/proof-of-stake) -CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) +// CreateNewBlock: create new block (without proof-of-work/with provided coinstake) +CBlock* CreateNewBlock(CWallet* pwallet, CTransaction *txCoinStake) { + bool fProofOfStake = txCoinStake != NULL; + // Create new block auto_ptr pblock(new CBlock()); if (!pblock.get()) return NULL; // Create coinbase tx - CTransaction txNew; - txNew.vin.resize(1); - txNew.vin[0].prevout.SetNull(); - txNew.vout.resize(1); + CTransaction txCoinBase; + txCoinBase.vin.resize(1); + txCoinBase.vin[0].prevout.SetNull(); + txCoinBase.vout.resize(1); if (!fProofOfStake) { CReserveKey reservekey(pwallet); - txNew.vout[0].scriptPubKey.SetDestination(reservekey.GetReservedKey().GetID()); + txCoinBase.vout[0].scriptPubKey.SetDestination(reservekey.GetReservedKey().GetID()); + + // Add our coinbase tx as first transaction + pblock->vtx.push_back(txCoinBase); } else - txNew.vout[0].SetEmpty(); + { + // Coinbase output must be empty for Proof-of-Stake block + txCoinBase.vout[0].SetEmpty(); - // Add our coinbase tx as first transaction - pblock->vtx.push_back(txNew); + // Syncronize timestamps + pblock->nTime = txCoinBase.nTime = txCoinStake->nTime; + + // Add coinbase and coinstake transactions + pblock->vtx.push_back(txCoinBase); + pblock->vtx.push_back(*txCoinStake); + } // Largest block you're willing to create: unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2); @@ -272,7 +285,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) continue; // Timestamp limit - if (tx.nTime > GetAdjustedTime() || (fProofOfStake && tx.nTime > pblock->vtx[0].nTime)) + if (tx.nTime > GetAdjustedTime() || (fProofOfStake && tx.nTime > txCoinStake->nTime)) continue; // Simplify transaction fee - allow free = false @@ -353,7 +366,7 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) pblock->vtx[0].vout[0].nValue = GetProofOfWorkReward(pblock->nBits, nFees); if (fDebug) - printf("CreateNewBlock(): reward %" PRIu64 "\n", pblock->vtx[0].vout[0].nValue); + printf("CreateNewBlock(): PoW reward %" PRIu64 "\n", pblock->vtx[0].vout[0].nValue); } if (fDebug && GetBoolArg("-printpriority")) @@ -361,10 +374,12 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake) // Fill in header pblock->hashPrevBlock = pindexPrev->GetBlockHash(); - pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); - pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime())); if (!fProofOfStake) + { + pblock->nTime = max(pindexPrev->GetMedianTimePast()+1, pblock->GetMaxTransactionTime()); + pblock->nTime = max(pblock->GetBlockTime(), PastDrift(pindexPrev->GetBlockTime())); pblock->UpdateTime(pindexPrev); + } pblock->nNonce = 0; } @@ -513,6 +528,124 @@ bool CheckStake(CBlock* pblock, CWallet& wallet) return true; } +// Precalculated SHA256 contexts and metadata +// (txid, vout.n) => (SHA256_CTX, (tx.nTime, nAmount)) +typedef std::map, std::pair > > MidstateMap; + +// Fill the inputs map with precalculated states and metadata +bool FillMap(CWallet *pwallet, uint32_t nUpperTime, MidstateMap &inputsMap) +{ + // Choose coins to use + int64_t nBalance = pwallet->GetBalance(); + + if (nBalance <= nReserveBalance) + return false; + + uint32_t nTime = GetAdjustedTime(); + + CTxDB txdb("r"); + { + LOCK2(cs_main, pwallet->cs_wallet); + + CoinsSet setCoins; + int64_t nValueIn = 0; + if (!pwallet->SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, nUpperTime, nCoinbaseMaturity * 10, setCoins, nValueIn)) + return error("FillMap() : SelectCoinsSimple failed"); + + if (setCoins.empty()) + return false; + + CBlock block; + CTxIndex txindex; + + for(CoinsSet::const_iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++) + { + pair key = make_pair(pcoin->first->GetHash(), pcoin->second); + + // Skip existent inputs + if (inputsMap.find(key) != inputsMap.end()) + continue; + + // Trying to parse scriptPubKey + txnouttype whichType; + vector vSolutions; + if (!Solver(pcoin->first->vout[pcoin->second].scriptPubKey, whichType, vSolutions)) + continue; + + // Only support pay to public key and pay to address + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) + continue; + + // Load transaction index item + if (!txdb.ReadTxIndex(pcoin->first->GetHash(), txindex)) + continue; + + // Read block header + if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) + continue; + + // Only load coins meeting min age requirement + if (nStakeMinAge + block.nTime > nTime - nMaxStakeSearchInterval) + continue; + + uint64_t nStakeModifier = 0; + if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier)) + continue; + + SHA256_CTX ctx; + // Calculate midstate using (modifier, block time, tx offset, tx time, output number) + GetKernelMidstate(nStakeModifier, block.nTime, txindex.pos.nTxPos - txindex.pos.nBlockPos, pcoin->first->nTime, pcoin->second, ctx); + + // (txid, vout.n) => (SHA256_CTX, (tx.nTime, nAmount)) + inputsMap[key] = make_pair(ctx, make_pair(pcoin->first->nTime, pcoin->first->vout[pcoin->second].nValue)); + } + + if (fDebug) + printf("Stake miner: %" PRIszu " precalculated contexts created\n", inputsMap.size()); + } + + return true; +} + +// Scan inputs map in order to find a solution +bool ScanMap(const MidstateMap &inputsMap, uint32_t nBits, MidstateMap::key_type &LuckyInput, std::pair &solution) +{ + static uint32_t nLastCoinStakeSearchTime = GetAdjustedTime(); // startup timestamp + uint32_t nSearchTime = GetAdjustedTime(); + + if (inputsMap.size() > 0 && nSearchTime > nLastCoinStakeSearchTime) + { + // Scanning interval (begintime, endtime) + std::pair interval; + + interval.first = nSearchTime; + interval.second = nSearchTime - min(nSearchTime-nLastCoinStakeSearchTime, nMaxStakeSearchInterval); + + // (txid, nout) => (SHA256_CTX, (tx.nTime, nAmount)) + for(MidstateMap::const_iterator input = inputsMap.begin(); input != inputsMap.end(); input++) + { + SHA256_CTX ctx = input->second.first; + + // scan(State, Bits, Time, Amount, ...) + if (ScanMidstateBackward(ctx, nBits, input->second.second.first, input->second.second.second, interval, solution)) + { + // Solution found + LuckyInput = input->first; // (txid, nout) + + return true; + } + } + + // Inputs map iteration can be big enough to consume few seconds while scanning. + // We're using dynamical calculation of scanning interval in order to compensate this delay. + nLastCoinStakeSearchInterval = nSearchTime - nLastCoinStakeSearchTime; + nLastCoinStakeSearchTime = nSearchTime; + } + + // No solutions were found + return false; +} + void StakeMiner(CWallet *pwallet) { SetThreadPriority(THREAD_PRIORITY_LOWEST); @@ -520,10 +653,14 @@ void StakeMiner(CWallet *pwallet) // Make this thread recognisable as the mining thread RenameThread("novacoin-miner"); + MidstateMap inputsMap; + if (!FillMap(pwallet, GetAdjustedTime(), inputsMap)) + return; + bool fTrySync = true; - // Each thread has its own counter - unsigned int nExtraNonce = 0; + CBlockIndex* pindexPrev = pindexBest; + uint32_t nBits = GetNextTargetRequired(pindexPrev, true); while (true) { @@ -556,26 +693,75 @@ void StakeMiner(CWallet *pwallet) } } - // - // Create new block - // - CBlockIndex* pindexPrev = pindexBest; - - auto_ptr pblock(CreateNewBlock(pwallet, true)); - if (!pblock.get()) - return; - IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce); + MidstateMap::key_type LuckyInput; + std::pair solution; - // Trying to sign a block - if (pblock->SignBlock(*pwallet)) + if (ScanMap(inputsMap, nBits, LuckyInput, solution)) { SetThreadPriority(THREAD_PRIORITY_NORMAL); - CheckStake(pblock.get(), *pwallet); + + // Remove lucky input from the map + inputsMap.erase(inputsMap.find(LuckyInput)); + + CKey key; + CTransaction txCoinStake; + + // Create new coinstake transaction + if (!pwallet->CreateCoinStake(LuckyInput.first, LuckyInput.second, solution.second, pindexPrev->nBits, txCoinStake, key)) + { + string strMessage = _("Warning: Unable to create coinstake transaction, see debug.log for the details. Mining thread has been stopped."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + + return; + } + + // Now we have new coinstake, it's time to create the block ... + CBlock* pblock; + pblock = CreateNewBlock(pwallet, &txCoinStake); + if (!pblock) + { + string strMessage = _("Warning: Unable to allocate memory for the new block object. Mining thread has been stopped."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + + return; + } + + unsigned int nExtraNonce = 0; + IncrementExtraNonce(pblock, pindexPrev, nExtraNonce); + + // ... and sign it + if (!key.Sign(pblock->GetHash(), pblock->vchBlockSig)) + { + string strMessage = _("Warning: Proof-of-Stake miner is unable to sign the block (locked wallet?). Mining thread has been stopped."); + strMiscWarning = strMessage; + printf("*** %s\n", strMessage.c_str()); + + return; + } + + CheckStake(pblock, *pwallet); SetThreadPriority(THREAD_PRIORITY_LOWEST); Sleep(500); } - else - Sleep(nMinerSleep); + + if (pindexPrev != pindexBest) + { + // The best block has been changed, we need to refill the map + if (FillMap(pwallet, GetAdjustedTime(), inputsMap)) + { + pindexPrev = pindexBest; + nBits = GetNextTargetRequired(pindexPrev, true); + } + else + { + // Clear existent data if FillMap failed + inputsMap.clear(); + } + } + + Sleep(500); continue; } diff --git a/src/miner.h b/src/miner.h index 915689e..abb81be 100644 --- a/src/miner.h +++ b/src/miner.h @@ -9,8 +9,8 @@ #include "main.h" #include "wallet.h" -/* Generate a new block, without valid proof-of-work */ -CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake=false); +/* Generate a new block, without valid proof-of-work/with provided proof-of-stake */ +CBlock* CreateNewBlock(CWallet* pwallet, CTransaction *txAdd=NULL); /** Modify the extranonce in a block */ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce); diff --git a/src/rpcwallet.cpp b/src/rpcwallet.cpp index 5607a4b..70bb52a 100644 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@ -15,6 +15,7 @@ using namespace std; int64_t nWalletUnlockTime; static CCriticalSection cs_nWalletUnlockTime; +extern int64_t nReserveBalance; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, json_spirit::Object& entry); std::string HelpRequiringPassphrase() @@ -1752,7 +1753,6 @@ Value reservebalance(const Array& params, bool fHelp) } Object result; - int64_t nReserveBalance = 0; if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) throw runtime_error("invalid reserve balance amount\n"); result.push_back(Pair("reserve", (nReserveBalance > 0))); diff --git a/src/wallet.cpp b/src/wallet.cpp index b484fc2..bfa0e24 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -16,9 +16,7 @@ #include "main.h" using namespace std; - - -bool fCoinsDataActual; +extern int64_t nReserveBalance; ////////////////////////////////////////////////////////////////////////////// // @@ -1856,174 +1854,90 @@ bool CWallet::MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const return true; } - -bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, uint32_t nSearchInterval, CTransaction& txNew, CKey& key) +bool CWallet::CreateCoinStake(uint256 &hashTx, uint32_t nOut, uint32_t nGenerationTime, uint32_t nBits, CTransaction &txNew, CKey& key) { - // The following combine threshold is important to security - // Should not be adjusted if you don't understand the consequences - int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; - - CBigNum bnTargetPerCoinDay; - bnTargetPerCoinDay.SetCompact(nBits); - - txNew.vin.clear(); - txNew.vout.clear(); - - // Mark coin stake transaction - CScript scriptEmpty; - scriptEmpty.clear(); - txNew.vout.push_back(CTxOut(0, scriptEmpty)); + CWalletTx wtx; + if (!GetTransaction(hashTx, wtx)) + return error("Transaction %s is not found\n", hashTx.GetHex().c_str()); - // Choose coins to use - int64_t nBalance = GetBalance(); - int64_t nReserveBalance = 0; + vector vSolutions; + txnouttype whichType; + CScript scriptPubKeyOut; + CScript scriptPubKeyKernel = wtx.vout[nOut].scriptPubKey; + if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) + return error("CreateCoinStake : failed to parse kernel\n"); - if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) - return error("CreateCoinStake : invalid reserve balance amount"); + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : parsed kernel type=%d\n", whichType); - if (nBalance <= nReserveBalance) - return false; + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) + return error("CreateCoinStake : no support for kernel type=%d\n", whichType); - vector vwtxPrev; - - CTxDB txdb("r"); + if (whichType == TX_PUBKEYHASH) // pay to address type { - LOCK2(cs_main, cs_wallet); - // Cache outputs unless best block or wallet transaction set changed - if (!fCoinsDataActual) - { - mapMeta.clear(); - int64_t nValueIn = 0; - CoinsSet setCoins; - if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, txNew.nTime, nCoinbaseMaturity * 10, setCoins, nValueIn)) - return false; - - if (setCoins.empty()) - return false; + // convert to pay to public key type + if (!GetKey(uint160(vSolutions[0]), key)) + return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); - { - CTxIndex txindex; - CBlock block; - for(CoinsSet::iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++) - { - // Load transaction index item - if (!txdb.ReadTxIndex(pcoin->first->GetHash(), txindex)) - continue; - - // Read block header - if (!block.ReadFromDisk(txindex.pos.nFile, txindex.pos.nBlockPos, false)) - continue; - - uint64_t nStakeModifier = 0; - if (!GetKernelStakeModifier(block.GetHash(), nStakeModifier)) - continue; - - // Add meta record - // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier)) - mapMeta[make_pair(pcoin->first->GetHash(), pcoin->second)] = make_pair(make_pair(txindex, *pcoin), make_pair(block, nStakeModifier)); + scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; + } + if (whichType == TX_PUBKEY) + { + valtype& vchPubKey = vSolutions[0]; + if (!GetKey(Hash160(vchPubKey), key)) + return error("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + if (key.GetPubKey() != vchPubKey) + return error("CreateCoinStake : invalid key for kernel type=%d\n", whichType); // keys mismatch + scriptPubKeyOut = scriptPubKeyKernel; + } - if (fDebug) - printf("Load coin: %s\n", pcoin->first->GetHash().GetHex().c_str()); - } - } + // The following combine threshold is important to security + // Should not be adjusted if you don't understand the consequences + int64_t nCombineThreshold = GetProofOfWorkReward(GetLastBlockIndex(pindexBest, false)->nBits) / 3; - if (fDebug) - printf("Stake miner: %" PRIszu " meta items loaded for %" PRIszu " coins\n", mapMeta.size(), setCoins.size()); + int64_t nBalance = GetBalance(); + int64_t nCredit = wtx.vout[nOut].nValue; - fCoinsDataActual = true; - nKernelsTried = 0; - nCoinDaysTried = 0; - } - } + txNew.vin.clear(); + txNew.vout.clear(); - int64_t nCredit = 0; - CScript scriptPubKeyKernel; + // List of constake dependencies + vector vwtxPrev; + vwtxPrev.push_back(&wtx); - unsigned int nTimeTx, nBlockTime; - COutPoint prevoutStake; - CoinsSet::value_type kernelcoin; + // Set generation time, and kernel input + txNew.nTime = nGenerationTime; + txNew.vin.push_back(CTxIn(hashTx, nOut)); - if (ScanForStakeKernelHash(mapMeta, nBits, txNew.nTime, nSearchInterval, kernelcoin, nTimeTx, nBlockTime, nKernelsTried, nCoinDaysTried)) - { - // Found a kernel - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : kernel found\n"); - vector vSolutions; - txnouttype whichType; - CScript scriptPubKeyOut; - scriptPubKeyKernel = kernelcoin.first->vout[kernelcoin.second].scriptPubKey; - if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) - { - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : failed to parse kernel\n"); - return false; - } - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : parsed kernel type=%d\n", whichType); - if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) - { - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : no support for kernel type=%d\n", whichType); - return false; // only support pay to public key and pay to address - } - if (whichType == TX_PUBKEYHASH) // pay to address type - { - // convert to pay to public key type - if (!keystore.GetKey(uint160(vSolutions[0]), key)) - { - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); - return false; // unable to find corresponding public key - } - scriptPubKeyOut << key.GetPubKey() << OP_CHECKSIG; - } - 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); - return false; // unable to find corresponding public key - } - if (key.GetPubKey() != vchPubKey) - { - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : invalid key for kernel type=%d\n", whichType); - return false; // keys mismatch - } - - scriptPubKeyOut = scriptPubKeyKernel; - } + // Mark coin stake transaction with empty vout[0] + CScript scriptEmpty; + scriptEmpty.clear(); + txNew.vout.push_back(CTxOut(0, scriptEmpty)); - txNew.nTime = nTimeTx; - txNew.vin.push_back(CTxIn(kernelcoin.first->GetHash(), kernelcoin.second)); - nCredit += kernelcoin.first->vout[kernelcoin.second].nValue; - vwtxPrev.push_back(kernelcoin.first); - txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : added kernel type=%d\n", whichType); - if (GetWeight((int64_t)nBlockTime, (int64_t)txNew.nTime) < nStakeMaxAge) - txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake - if (fDebug && GetBoolArg("-printcoinstake")) - printf("CreateCoinStake : added kernel type=%d\n", whichType); - } + int64_t nValueIn = 0; + CoinsSet setCoins; + if (!SelectCoinsSimple(nBalance - nReserveBalance, MIN_TX_FEE, MAX_MONEY, nGenerationTime, nCoinbaseMaturity * 10, setCoins, nValueIn)) + return false; - if (nCredit == 0 || nCredit > nBalance - nReserveBalance) + if (setCoins.empty()) return false; - // (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier)) - for(MetaMap::const_iterator meta_item = mapMeta.begin(); meta_item != mapMeta.end(); meta_item++) + bool fMaxTimeWeight = false; + if (GetWeight((int64_t)wtx.nTime, (int64_t)nGenerationTime) == nStakeMaxAge) { - // Get coin - CoinsSet::value_type pcoin = meta_item->second.first.second; + // Only one output for old kernel inputs + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); - // 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) + // Iterate through set of (wtx*, nout) in order to find some additional inputs for our new coinstake transaction. + // + // * Value is higher than 0.01 NVC; + // * Only add inputs of the same key/address as kernel; + // * Input hash and kernel parent hash should be different. + for(CoinsSet::iterator pcoin = setCoins.begin(); pcoin != setCoins.end(); pcoin++) { - int64_t nTimeWeight = GetWeight((int64_t)pcoin.first->nTime, (int64_t)txNew.nTime); - // Stop adding more inputs if already too many inputs if (txNew.vin.size() >= 100) break; @@ -2031,60 +1945,72 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, uin 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) - continue; + + int64_t nTimeWeight = GetWeight((int64_t)pcoin->first->nTime, (int64_t)nGenerationTime); + // Do not add input that is still too young if (nTimeWeight < nStakeMaxAge) continue; + // Do not add input if key/address is not the same as kernel + if (pcoin->first->vout[pcoin->second].scriptPubKey != scriptPubKeyKernel && pcoin->first->vout[pcoin->second].scriptPubKey != txNew.vout[1].scriptPubKey) + continue; + // Do not add input if parents are the same + if (pcoin->first->GetHash() != txNew.vin[0].prevout.hash) + continue; + // Do not add additional significant input + if (pcoin->first->vout[pcoin->second].nValue > nCombineThreshold) + 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); } - } - // Calculate coin age reward + fMaxTimeWeight = true; + } + else { - uint64_t nCoinAge; - CTxDB txdb("r"); - if (!txNew.GetCoinAge(txdb, nCoinAge)) - return error("CreateCoinStake : failed to calculate coin age"); - - int64_t nReward = GetProofOfStakeReward(nCoinAge, nBits, txNew.nTime); - // Refuse to create mint that has zero or negative reward - if(nReward <= 0) - return false; - - nCredit += nReward; + // Split stake input if maximum weight isn't reached yet + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + txNew.vout.push_back(CTxOut(0, scriptPubKeyOut)); + + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : maximum time weight isn't reached, splitting coinstake\n"); } + // Calculate coin age reward + uint64_t nCoinAge; + CTxDB txdb("r"); + if (!txNew.GetCoinAge(txdb, nCoinAge)) + return error("CreateCoinStake : failed to calculate coin age\n"); + nCredit += GetProofOfStakeReward(nCoinAge, nBits, nGenerationTime); + int64_t nMinFee = 0; while (true) { // Set output amount - if (txNew.vout.size() == 3) + if (fMaxTimeWeight) + txNew.vout[1].nValue = nCredit - nMinFee; + else { txNew.vout[1].nValue = ((nCredit - nMinFee) / 2 / CENT) * CENT; txNew.vout[2].nValue = nCredit - nMinFee - txNew.vout[1].nValue; } - else - txNew.vout[1].nValue = nCredit - nMinFee; // Sign int nIn = 0; BOOST_FOREACH(const CWalletTx* pcoin, vwtxPrev) { if (!SignSignature(*this, *pcoin, txNew, nIn++)) - return error("CreateCoinStake : failed to sign coinstake"); + return error("CreateCoinStake : failed to sign coinstake\n"); } // Limit size unsigned int nBytes = ::GetSerializeSize(txNew, SER_NETWORK, PROTOCOL_VERSION); if (nBytes >= MAX_BLOCK_SIZE_GEN/5) - return error("CreateCoinStake : exceeded coinstake size limit"); + return error("CreateCoinStake : exceeded coinstake size limit\n"); // Check enough fee is paid if (nMinFee < txNew.GetMinFee(1, false, GMF_BLOCK, nBytes) - CENT) @@ -2100,11 +2026,10 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, uin } } - // Successfully generated coinstake + // Successfully created coinstake return true; } - // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) { diff --git a/src/wallet.h b/src/wallet.h index cb7c238..9789c51 100644 --- a/src/wallet.h +++ b/src/wallet.h @@ -30,11 +30,6 @@ class CCoinControl; // Set of selected transactions typedef std::set > CoinsSet; -// Preloaded coins metadata -// (txid, vout.n) => ((txindex, (tx, vout.n)), (block, modifier)) -typedef std::map, std::pair >, std::pair > > MetaMap; - - /** (client) version numbers for particular wallet features */ enum WalletFeature { @@ -78,7 +73,6 @@ public: class CWallet : public CCryptoKeyStore { private: - bool SelectCoinsSimple(int64_t nTargetValue, int64_t nMinValue, int64_t nMaxValue, unsigned int nSpendTime, int nMinConf, std::set >& setCoinsRet, int64_t& nValueRet) const; bool SelectCoins(int64_t nTargetValue, unsigned int nSpendTime, std::set >& setCoinsRet, int64_t& nValueRet, const CCoinControl *coinControl=NULL) const; CWalletDB *pwalletdbEncryption, *pwalletdbDecryption; @@ -89,9 +83,6 @@ private: // the maximum wallet format version: memory-only variable that specifies to what version this wallet may be upgraded int nWalletMaxVersion; - // selected coins metadata - std::map, std::pair >, std::pair > > mapMeta; - // stake mining statistics uint64_t nKernelsTried; uint64_t nCoinDaysTried; @@ -105,7 +96,6 @@ public: std::set setKeyPool; std::map mapKeyMetadata; - typedef std::map MasterKeyMap; MasterKeyMap mapMasterKeys; unsigned int nMasterKeyMaxID; @@ -152,6 +142,10 @@ public: void AvailableCoinsMinConf(std::vector& vCoins, int nConf, int64_t nMinValue, int64_t nMaxValue) const; void AvailableCoins(std::vector& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const; bool SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, std::vector vCoins, std::set >& setCoinsRet, int64_t& nValueRet) const; + + // Simple select (without randomization) + bool SelectCoinsSimple(int64_t nTargetValue, int64_t nMinValue, int64_t nMaxValue, unsigned int nSpendTime, int nMinConf, std::set >& setCoinsRet, int64_t& nValueRet) const; + // keystore implementation // Generate a new key CPubKey GenerateNewKey(); @@ -225,7 +219,7 @@ public: void GetStakeStats(float &nKernelsRate, float &nCoinDaysRate); void GetStakeWeightFromValue(const int64_t& nTime, const int64_t& nValue, uint64_t& nWeight); - bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, uint32_t nSearchInterval, CTransaction& txNew, CKey& key); + bool CreateCoinStake(uint256 &hashTx, uint32_t nOut, uint32_t nTime, uint32_t nBits, CTransaction &txNew, CKey& key); bool MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const int64_t& nMaxValue, std::list& listMerged); std::string SendMoney(CScript scriptPubKey, int64_t nValue, CWalletTx& wtxNew, bool fAskFee=false); -- 1.7.1