Merge remote-tracking branch 'remotes/origin/newminer'
authorCryptoManiac <balthazar@yandex.ru>
Fri, 24 Apr 2015 20:22:03 +0000 (13:22 -0700)
committerCryptoManiac <balthazar@yandex.ru>
Fri, 24 Apr 2015 20:22:03 +0000 (13:22 -0700)
1  2 
src/kernel.cpp
src/main.cpp
src/rpcwallet.cpp
src/wallet.cpp

diff --combined 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
              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; n<nCurrentSearchInterval && fCoinsDataActual && !fShutdown; n++)
-         {
-             nTimeTx = nTime - n;
-             CBigNum bnCoinDayWeight = CBigNum(nValueIn) * GetWeight((int64_t)pcoin.first->nTime, (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<uint32_t, uint32_t> &SearchInterval, std::pair<uint256, uint32_t> &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<uint32_t>(hashChecksum.Get64());
  }
  
  // Check stake modifier hard checkpoints
diff --combined 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<unsigned int>::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<CTransaction>::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<uint256, CBlockIndex*>::iterator it1 = mapBlockIndex.begin();
 +        for (; it1 != mapBlockIndex.end(); it1++)
 +            delete (*it1).second;
 +        mapBlockIndex.clear();
 +
 +        // orphan blocks
 +        std::map<uint256, CBlock*>::iterator it2 = mapOrphanBlocks.begin();
 +        for (; it2 != mapOrphanBlocks.end(); it2++)
 +            delete (*it2).second;
 +        mapOrphanBlocks.clear();
 +
 +        // orphan transactions
 +    }
 +} instance_of_cmaincleanup;
diff --combined 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
              "<amount> is resulting inputs sum\n"
              "<minvalue> is minimum value of inputs which are used in join process\n"
              "<outputvalue> 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())
      // 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 <novacoinaddress> <amount> [comment] [comment-to]\n"
 -            "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
 +            "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
              + HelpRequiringPassphrase());
  
      CBitcoinAddress address(params[0].get_str());
      // 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 <fromaccount> <tonovacoinaddress> <amount> [minconf=1] [comment] [comment-to]\n"
 -            "<amount> is a real and is rounded to the nearest " + FormatMoney(MIN_TXOUT_AMOUNT)
 +            "<amount> is a real and is rounded to the nearest " + FormatMoney(nMinimumInputValue)
              + HelpRequiringPassphrase());
  
      string strAccount = AccountFromValue(params[0]);
          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
@@@ -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);
          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<uint256>& listMerged)
  {
      int64_t nBalance = GetBalance();
      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<valtype> 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<const CWalletTx*> 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<const CWalletTx*> 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<valtype> 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;
              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)
          }
      }
  
-     // Successfully generated coinstake
+     // Successfully created coinstake
      return true;
  }
  
  // Call after CreateTransaction unless you want to abort
  bool CWallet::CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey)
  {