From: CryptoManiac Date: Fri, 24 Apr 2015 20:22:03 +0000 (-0700) Subject: Merge remote-tracking branch 'remotes/origin/newminer' X-Git-Tag: nvc-v0.5.3~22 X-Git-Url: https://git.novaco.in/?p=novacoin.git;a=commitdiff_plain;h=9417ff30cae6ffffd1c4fc4f4b1a81551f04dbc5;hp=-c Merge remote-tracking branch 'remotes/origin/newminer' --- 9417ff30cae6ffffd1c4fc4f4b1a81551f04dbc5 diff --combined src/kernel.cpp index 58a4b88,4e653a4..a7df10e --- a/src/kernel.cpp +++ b/src/kernel.cpp @@@ -94,18 -94,6 +94,6 @@@ bool IsFixedModifierInterval(unsigned i 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 +398,10 @@@ bool CheckStakeKernelHash(uint32_t nBit 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 +414,14 @@@ 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) { @@@ -557,6 -470,53 +470,53 @@@ bool ScanMidstateForward(SHA256_CTX &ct 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; + } + + // 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)) @@@ -617,7 -577,7 +577,7 @@@ uint32_t GetStakeModifierChecksum(cons ss << pindex->nFlags << pindex->hashProofOfStake << pindex->nStakeModifier; uint256 hashChecksum = Hash(ss.begin(), ss.end()); hashChecksum >>= (256 - 32); - return hashChecksum.Get64(); + return static_cast(hashChecksum.Get64()); } // Check stake modifier hard checkpoints diff --combined src/main.cpp index 78364d7,a62d641..e95376e --- a/src/main.cpp +++ b/src/main.cpp @@@ -77,7 -77,7 +77,7 @@@ const string strMessageMagic = "NovaCoi // Settings int64_t nTransactionFee = MIN_TX_FEE; -int64_t nMinimumInputValue = MIN_TX_FEE; +int64_t nMinimumInputValue = MIN_TXOUT_AMOUNT; // Ping and address broadcast intervals int64_t nPingInterval = 30 * 60; @@@ -151,8 -151,6 +151,6 @@@ void SyncWithWallets(const CTransaction 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 +1034,13 @@@ int64_t GetProofOfWorkReward(unsigned i // 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 +1080,6 @@@ int64_t GetProofOfStakeReward(int64_t n 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) { // @@@ -2387,7 -2382,7 +2382,7 @@@ bool CBlock::AcceptBlock( // Write block to history file if (!CheckDiskSpace(::GetSerializeSize(*this, SER_DISK, CLIENT_VERSION))) return error("AcceptBlock() : out of disk space"); - unsigned int nFile = -1; + unsigned int nFile = std::numeric_limits::max(); unsigned int nBlockPos = 0; if (!WriteToDisk(nFile, nBlockPos)) return error("AcceptBlock() : WriteToDisk failed"); @@@ -2627,56 -2622,6 +2622,6 @@@ bool ProcessBlock(CNode* pfrom, CBlock 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 { @@@ -4103,25 -4048,3 +4048,25 @@@ bool SendMessages(CNode* pto, bool fSen } return true; } + + +class CMainCleanup +{ +public: + CMainCleanup() {} + ~CMainCleanup() { + // block headers + std::map::iterator it1 = mapBlockIndex.begin(); + for (; it1 != mapBlockIndex.end(); it1++) + delete (*it1).second; + mapBlockIndex.clear(); + + // orphan blocks + std::map::iterator it2 = mapOrphanBlocks.begin(); + for (; it2 != mapOrphanBlocks.end(); it2++) + delete (*it2).second; + mapOrphanBlocks.clear(); + + // orphan transactions + } +} instance_of_cmaincleanup; diff --combined src/rpcwallet.cpp index f6a0383,70bb52a..3edfb17 --- a/src/rpcwallet.cpp +++ b/src/rpcwallet.cpp @@@ -15,6 -15,7 +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() @@@ -263,7 -264,7 +264,7 @@@ Value mergecoins(const Array& params, b " is resulting inputs sum\n" " is minimum value of inputs which are used in join process\n" " is resulting value of inputs which will be created\n" - "All values are real and and rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT) + "All values are real and and rounded to the nearest " + FormatMoney(nMinimumInputValue) + HelpRequiringPassphrase()); if (pwalletMain->IsLocked()) @@@ -278,13 -279,13 +279,13 @@@ // Output amount int64_t nOutputValue = AmountFromValue(params[2]); - if (nAmount < MIN_TXOUT_AMOUNT) + if (nAmount < nMinimumInputValue) throw JSONRPCError(-101, "Send amount too small"); - if (nMinValue < MIN_TXOUT_AMOUNT) + if (nMinValue < nMinimumInputValue) throw JSONRPCError(-101, "Max value too small"); - if (nOutputValue < MIN_TXOUT_AMOUNT) + if (nOutputValue < nMinimumInputValue) throw JSONRPCError(-101, "Output value too small"); if (nOutputValue < nMinValue) @@@ -306,7 -307,7 +307,7 @@@ Value sendtoaddress(const Array& params if (fHelp || params.size() < 2 || params.size() > 4) throw runtime_error( "sendtoaddress [comment] [comment-to]\n" - " is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT) + " is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue) + HelpRequiringPassphrase()); CBitcoinAddress address(params[0].get_str()); @@@ -316,7 -317,7 +317,7 @@@ // Amount int64_t nAmount = AmountFromValue(params[1]); - if (nAmount < MIN_TXOUT_AMOUNT) + if (nAmount < nMinimumInputValue) throw JSONRPCError(-101, "Send amount too small"); // Wallet comments @@@ -629,7 -630,7 +630,7 @@@ Value movecmd(const Array& params, boo string strTo = AccountFromValue(params[1]); int64_t nAmount = AmountFromValue(params[2]); - if (nAmount < MIN_TXOUT_AMOUNT) + if (nAmount < nMinimumInputValue) throw JSONRPCError(-101, "Send amount too small"); if (params.size() > 3) @@@ -677,7 -678,7 +678,7 @@@ Value sendfrom(const Array& params, boo if (fHelp || params.size() < 3 || params.size() > 6) throw runtime_error( "sendfrom [minconf=1] [comment] [comment-to]\n" - " is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT) + " is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue) + HelpRequiringPassphrase()); string strAccount = AccountFromValue(params[0]); @@@ -686,7 -687,7 +687,7 @@@ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid NovaCoin address"); int64_t nAmount = AmountFromValue(params[2]); - if (nAmount < MIN_TXOUT_AMOUNT) + if (nAmount < nMinimumInputValue) throw JSONRPCError(-101, "Send amount too small"); int nMinDepth = 1; @@@ -753,7 -754,7 +754,7 @@@ Value sendmany(const Array& params, boo scriptPubKey.SetDestination(address.Get()); int64_t nAmount = AmountFromValue(s.value_); - if (nAmount < MIN_TXOUT_AMOUNT) + if (nAmount < nMinimumInputValue) throw JSONRPCError(-101, "Send amount too small"); totalAmount += nAmount; @@@ -1752,7 -1753,6 +1753,6 @@@ Value reservebalance(const Array& param } 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 --combined src/wallet.cpp index 88d661e,97648c8..deb5376 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@@ -16,9 -16,7 +16,7 @@@ #include "main.h" using namespace std; - - - bool fCoinsDataActual; + extern int64_t nReserveBalance; ////////////////////////////////////////////////////////////////////////////// // @@@ -1081,7 -1079,7 +1079,7 @@@ void CWallet::ResendWalletTransactions( { // Do this infrequently and randomly to avoid giving away // that these are our transactions. - static int64_t nNextTime; + static int64_t nNextTime = GetRand(GetTime() + 30 * 60); if (GetTime() < nNextTime) return; bool fFirst = (nNextTime == 0); @@@ -1090,7 -1088,7 +1088,7 @@@ return; // Only do it if there's been a new block since last time - static int64_t nLastTime; + static int64_t nLastTime = 0; if (nTimeBestReceived < nLastTime) return; nLastTime = GetTime(); @@@ -1699,40 -1697,6 +1697,6 @@@ void CWallet::GetStakeWeightFromValue(c nWeight = bnCoinDayWeight.getuint64(); } - - // NovaCoin: get current stake miner statistics - void CWallet::GetStakeStats(float &nKernelsRate, float &nCoinDaysRate) - { - static uint64_t nLastKernels = 0, nLastCoinDays = 0; - static float nLastKernelsRate = 0, nLastCoinDaysRate = 0; - static int64_t nLastTime = GetTime(); - - if (nKernelsTried < nLastKernels) - { - nLastKernels = 0; - nLastCoinDays = 0; - - nLastTime = GetTime(); - } - - int64_t nInterval = GetTime() - nLastTime; - //if (nKernelsTried > 1000 && nInterval > 5) - if (nInterval > 10) - { - nKernelsRate = nLastKernelsRate = ( nKernelsTried - nLastKernels ) / (float) nInterval; - nCoinDaysRate = nLastCoinDaysRate = ( nCoinDaysTried - nLastCoinDays ) / (float) nInterval; - - nLastKernels = nKernelsTried; - nLastCoinDays = nCoinDaysTried; - nLastTime = GetTime(); - } - else - { - nKernelsRate = nLastKernelsRate; - nCoinDaysRate = nLastCoinDaysRate; - } - } - bool CWallet::MergeCoins(const int64_t& nAmount, const int64_t& nMinValue, const int64_t& nOutputValue, list& listMerged) { int64_t nBalance = GetBalance(); @@@ -1856,174 -1820,90 +1820,90 @@@ 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; + CWalletTx wtx; + if (!GetTransaction(hashTx, wtx)) + return error("Transaction %s is not found\n", hashTx.GetHex().c_str()); - CBigNum bnTargetPerCoinDay; - bnTargetPerCoinDay.SetCompact(nBits); + 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"); - txNew.vin.clear(); - txNew.vout.clear(); + if (fDebug && GetBoolArg("-printcoinstake")) + printf("CreateCoinStake : parsed kernel type=%d\n", whichType); - // Mark coin stake transaction - CScript scriptEmpty; - scriptEmpty.clear(); - txNew.vout.push_back(CTxOut(0, scriptEmpty)); + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) + return error("CreateCoinStake : no support for kernel type=%d\n", whichType); - // Choose coins to use - int64_t nBalance = GetBalance(); - int64_t nReserveBalance = 0; - - if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) - return error("CreateCoinStake : invalid reserve balance amount"); - - if (nBalance <= nReserveBalance) - return false; - - 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; - - { - 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; + // 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); - 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)); - - if (fDebug) - printf("Load coin: %s\n", pcoin->first->GetHash().GetHex().c_str()); - } - } + 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("Stake miner: %" PRIszu " meta items loaded for %" PRIszu " coins\n", mapMeta.size(), setCoins.size()); + // 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; - fCoinsDataActual = true; - nKernelsTried = 0; - nCoinDaysTried = 0; - } - } + int64_t nBalance = GetBalance(); + int64_t nCredit = wtx.vout[nOut].nValue; - int64_t nCredit = 0; - CScript scriptPubKeyKernel; + txNew.vin.clear(); + txNew.vout.clear(); - unsigned int nTimeTx, nBlockTime; - COutPoint prevoutStake; - CoinsSet::value_type kernelcoin; + // List of constake dependencies + vector vwtxPrev; + vwtxPrev.push_back(&wtx); - 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 - } + // Set generation time, and kernel input + txNew.nTime = nGenerationTime; + txNew.vin.push_back(CTxIn(hashTx, nOut)); - 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 -1911,72 +1911,72 @@@ 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 -1992,10 +1992,10 @@@ } } - // Successfully generated coinstake + // Successfully created coinstake return true; } - // Call after CreateTransaction unless you want to abort bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey) {