diff --git a/src/miner.cpp b/src/miner.cpp index 895d3eefa..54bd275cc 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -49,7 +49,7 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam int64_t nSearchTime = txCoinStake.nTime; // search to current time if (nSearchTime > nLastCoinStakeSearchTime) { bool gotCoinStake = false; - GetMainSignals().CreateCoinStake(pblock->nBits, nSearchTime - nLastCoinStakeSearchTime, &txCoinStake, gotCoinStake); + GetMainSignals().CreateCoinStake(pblock->nBits, nSearchTime - nLastCoinStakeSearchTime, &txCoinStake, gotCoinStake); if (gotCoinStake) { if (txCoinStake.nTime >= std::max(pindexPrev->GetMedianTimePast() + 1, pindexPrev->GetBlockTime() - GetMaxClockDrift(pindexPrev->nHeight + 1))) { // make sure coinstake would meet timestamp protocol // as it would be the same as the block timestamp diff --git a/src/pos.cpp b/src/pos.cpp index 12e62993b..916c14e03 100644 --- a/src/pos.cpp +++ b/src/pos.cpp @@ -496,7 +496,7 @@ bool GetCoinAge(uint64_t& nCoinAge, CTransactionRef tx) CDiskBlockPos blockPos = pblockindex->GetBlockPos(); if (!ReadBlockFromDisk(blockPrev, blockPos, Params().GetConsensus())) - return false; // unable to read block of previous transaction + return error("CheckProofOfStake() : read block failed"); if (blockPrev.GetBlockTime() + Params().GetConsensus().nStakeMinAge > tx->nTime) continue; // only count coins meeting min age requirement diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 51785cfa9..0c0a8e2f8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -41,6 +41,8 @@ #include #include +#include +#include using interfaces::FoundBlock; @@ -58,6 +60,12 @@ static RecursiveMutex cs_wallets; static std::vector> vpwallets GUARDED_BY(cs_wallets); static std::list g_load_wallet_fns GUARDED_BY(cs_wallets); +/** Functions for disk access for blocks */ +extern bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); +extern bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); +extern bool ReadRawBlockFromDisk(std::vector& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start); +extern bool ReadRawBlockFromDisk(std::vector& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start); + bool AddWalletSetting(interfaces::Chain& chain, const std::string& wallet_name) { util::SettingsValue setting_value = chain.getRwSetting("wallet"); @@ -201,6 +209,194 @@ void CWallet::CreateCoinStake(unsigned int nBits, int64_t nSearchInterval, CTran return; } + arith_uint256 bnTargetPerCoinDay; + bnTargetPerCoinDay.SetCompact(nBits); + + CMutableTransaction mtx(*txNew); + + mtx.vin.clear(); + mtx.vout.clear(); + // Mark coin stake transaction + CScript scriptEmpty; + scriptEmpty.clear(); + mtx.vout.push_back(CTxOut(0, scriptEmpty)); + + // Choose coins to use + int64_t nBalance = GetBalance(); + int64_t nReserveBalance = 0; + // todo: cloak - wire up args + /* + if (mapArgs.count("-reservebalance") && !ParseMoney(mapArgs["-reservebalance"], nReserveBalance)) + return error("CreateCoinStake : invalid reserve balance amount"); + if (nBalance <= nReserveBalance) + return false; + */ + + //std::set > setCoins; + std::set setCoins; + std::vector vwtxPrev; + int64_t nValueIn = 0; + //if (!SelectCoins(nBalance - nReserveBalance, txNew.nTime, setCoins, nValueIn)) + + std::vector vCoins; + AvailableCoins(vCoins, true); + CCoinControl coin_control; + CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy + bool bnbWasUsed = false; + if (!SelectCoins(vCoins, nBalance - nReserveBalance, setCoins, nValueIn, coin_control, coin_selection_params, bnbWasUsed)) { + result = false; + return; + } + + if (setCoins.empty()) { + result = false; + return; + } + + int64_t nCredit = 0; + CScript scriptPubKeyKernel; + + for (const auto& entry : setCoins) { + uint256 hashPrevTx = entry.outpoint.hash; + uint256 hashPrevBlock; + CTransactionRef txPrev; + + // get input tx and block ref containing for block including input tx + if (!GetTransaction(hashPrevTx, txPrev, Params().GetConsensus(), hashPrevBlock, true)) { + result = false; + error("CreateCoinStake() : INFO: read txPrev failed"); // previous transaction not in main chain, may occur during initial download + return; + } + + CBlockIndex* pblockindex = mapBlockIndex[hashPrevBlock]; + CBlock blockPrev; + CDiskBlockPos blockPos = pblockindex->GetBlockPos(); + + if (!ReadBlockFromDisk(blockPrev, blockPos, Params().GetConsensus())) { + result = false; + error("CreateCoinStake() : read block failed"); + return; + } + + unsigned int prevTxOffsetIndex = 0; + + int prevTxOffsetInBlock = blockPos.nPos + GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION) - (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(blockPrev.vtx.size()); + for (auto& i : blockPrev.vtx) { + if (i->GetHash() == txPrev->GetHash()) + break; + prevTxOffsetInBlock += GetSerializeSize(i, SER_DISK, CLIENT_VERSION); // file pos of tx in block + prevTxOffsetIndex++; // tx index in block vtx + } + + static int nMaxStakeSearchInterval = 60; + + // printf(">> block.GetBlockTime() = %"PRI64d", nStakeMinAge = %d, txNew.nTime = %d\n", block.GetBlockTime(), nStakeMinAge,txNew.nTime); + if (blockPrev.GetBlockTime() + Params().GetConsensus().nStakeMinAge > mtx.nTime - nMaxStakeSearchInterval) + continue; // only count coins meeting min age requirement + + bool fKernelFound = false; + for (unsigned int n = 0; n < std::min(nSearchInterval, (int64_t)nMaxStakeSearchInterval) && !fKernelFound && !ShutdownRequested(); n++) { + // printf(">> In.....\n"); + // Search backward in time from the given txNew timestamp + // Search nSearchInterval seconds back up to nMaxStakeSearchInterval + uint256 hashProofOfStake = uint256S("0"); + COutPoint prevoutStake = COutPoint(hashPrevTx, prevTxOffsetIndex); + if (CheckStakeKernelHash(nBits, pblockindex, prevTxOffsetInBlock - blockPos.nPos, MakeTransactionRef(mtx), prevoutStake, mtx.nTime - n, hashProofOfStake)) { + // Found a kernel + if (gArgs.GetBoolArg("-printcoinstake", false)) + printf("CreateCoinStake : kernel found\n"); + std::vector vSolutions; + txnouttype whichType; + CScript scriptPubKeyOut; + scriptPubKeyKernel = entry.txout.scriptPubKey; + if (!Solver(scriptPubKeyKernel, whichType, vSolutions)) { + if (gArgs.GetBoolArg("-printcoinstake", false)) + printf("CreateCoinStake : failed to parse kernel\n"); + break; + } + if (gArgs.GetBoolArg("-printcoinstake", false)) + printf("CreateCoinStake : parsed kernel type=%d\n", whichType); + if (whichType != TX_PUBKEY && whichType != TX_PUBKEYHASH) { + if (gArgs.GetBoolArg("-printcoinstake", false)) + printf("CreateCoinStake : no support for kernel type=%d\n", whichType); + break; // 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 + CKey key; + if (!GetKey(CKeyID(uint160(vSolutions[0])), key)) { + if (gArgs.GetBoolArg("-printcoinstake", false)) + printf("CreateCoinStake : failed to get key for kernel type=%d\n", whichType); + break; // unable to find corresponding public key + } + + std::vector vch; + CPubKey pk = key.GetPubKey(); + scriptPubKeyOut << std::vector(pk.data(), pk.data() + pk.size()) << OP_CHECKSIG; + } else + scriptPubKeyOut = scriptPubKeyKernel; + + mtx.nTime -= n; + mtx.vin.push_back(CTxIn(hashPrevTx, prevTxOffsetIndex)); + nCredit += entry.txout.nValue; + + // printf(">> Wallet: CreateCoinStake: nCredit = %"PRI64d"\n", nCredit); + + vwtxPrev.push_back(entry); + mtx.vout.push_back(CTxOut(0, scriptPubKeyOut)); + if (blockPrev.GetBlockTime() + nStakeSplitAge > mtx.nTime) + mtx.vout.push_back(CTxOut(0, scriptPubKeyOut)); //split stake + + if (gArgs.GetBoolArg("-printcoinstake", false)) + printf("CreateCoinStake : added kernel type=%d\n", whichType); + fKernelFound = true; + break; + } + } + if (fKernelFound || ShutdownRequested()) + break; // if kernel is found stop searching + } + if (nCredit == 0 || nCredit > nBalance - nReserveBalance) { + // printf(">> Wallet: CreateCoinStake: nCredit = %"PRI64d", nBalance = %"PRI64d", nReserveBalance = %"PRI64d"\n", nCredit, nBalance, nReserveBalance); + result = false; + return; + } + + for (const auto& entry : setCoins) { + uint256 hashPrevTx = entry.outpoint.hash; + + // Attempt to add more inputs + // Only add coins of the same key/address as kernel + if (mtx.vout.size() == 2 && ((entry.txout.scriptPubKey == scriptPubKeyKernel || entry.txout.scriptPubKey == mtx.vout[1].scriptPubKey)) && entry.outpoint.hash != mtx.vin[0].prevout.hash) { + // Stop adding more inputs if already too many inputs + if (mtx.vin.size() >= 100) + break; + // Stop adding more inputs if value is already pretty significant + if (nCredit > nCombineThreshold) + break; + // Stop adding inputs if reached reserve limit + if (nCredit + entry.txout.nValue > nBalance - nReserveBalance) + break; + // Do not add additional significant input + if (entry.txout.nValue > nCombineThreshold) + continue; + // Do not add input that is still too young + if (pcoin.first->nTime + nStakeMaxAge > mtx.nTime) + continue; + // Do not add coins that are reserved for Enigma + if (pcoin.first->IsEnigmaReserved(pcoin.second)) + continue; + + mtx.vin.push_back(CTxIn(pcoin.first->GetHash(), pcoin.second)); + nCredit += pcoin.first->vout[pcoin.second].nValue; + vwtxPrev.push_back(pcoin.first); + } + } + + txNew = MakeTransactionRef(mtx); + result = true; + int64_t nCredit = 0; CScript scriptPubKeyKernel;