From 61362bef05b5e6c1bc34eb07aa5145732425f543 Mon Sep 17 00:00:00 2001 From: lateminer Date: Tue, 19 Mar 2019 23:54:30 +0300 Subject: [PATCH] PoS: get rid of nStakeMinConfirmations, IsConfirmedInNPrevBlocks(), ReadFromDisk() --- src/chainparams.cpp | 3 -- src/consensus/params.h | 1 - src/main.cpp | 37 +--------------- src/main.h | 2 - src/pos.cpp | 97 ++++++++++++++++++++---------------------- src/pos.h | 12 +++--- src/wallet/wallet.cpp | 27 ++++++------ src/wallet/wallet.h | 3 ++ 8 files changed, 66 insertions(+), 116 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c489161f0..f4583cd7f 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -118,7 +118,6 @@ public: consensus.nLastPOWBlock = 10000; consensus.nStakeTimestampMask = 0xf; // 15 consensus.nCoinbaseMaturity = 500; - consensus.nStakeMinConfirmations = 500; consensus.nStakeMinAge = 8 * 60 * 60; // 8 hours // The best chain should have at least this much work. @@ -209,7 +208,6 @@ public: consensus.nLastPOWBlock = 0x7fffffff; consensus.nStakeTimestampMask = 0xf; consensus.nCoinbaseMaturity = 10; - consensus.nStakeMinConfirmations = 10; consensus.nStakeMinAge = 8 * 60 * 60; pchMessageStart[0] = 0xcd; @@ -307,7 +305,6 @@ public: consensus.nLastPOWBlock = 1000; consensus.nStakeTimestampMask = 0xf; consensus.nCoinbaseMaturity = 10; - consensus.nStakeMinConfirmations = 10; consensus.nStakeMinAge = 1 * 60 * 60; pchMessageStart[0] = 0x70; diff --git a/src/consensus/params.h b/src/consensus/params.h index e59236eed..72d4d6729 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -73,7 +73,6 @@ struct Params { int nLastPOWBlock; int nStakeTimestampMask; int nCoinbaseMaturity; - int nStakeMinConfirmations; unsigned int nStakeMinAge; uint256 nMinimumChainWork; }; diff --git a/src/main.cpp b/src/main.cpp index 5f8737b53..a4ffffea0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1679,41 +1679,6 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus return true; } -bool ReadFromDisk(CTransaction& tx, CDiskTxPos& txindex, CBlockTreeDB& txdb, COutPoint prevout) -{ - if (!txdb.ReadTxIndex(prevout.hash, txindex)){ - LogPrintf("no tx index %s \n", prevout.hash.ToString()); - return false; - } - if (!ReadFromDisk(tx, txindex)) - return false; - if (prevout.n >= tx.vout.size()) - { - return false; - } - return true; -} - -bool ReadFromDisk(CTransaction& tx, CDiskTxPos& txindex) -{ - CAutoFile filein(OpenBlockFile(txindex, true), SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - return error("CTransaction::ReadFromDisk() : OpenBlockFile failed"); - - // Read transaction - CBlockHeader header; - try { - filein >> header; - fseek(filein.Get(), txindex.nTxOffset, SEEK_CUR); - filein >> tx; - } - catch (const std::exception& e) { - return error("%s: Deserialize or I/O error - %s", __func__, e.what()); - } - - return true; -} - CAmount GetProofOfWorkSubsidy() { return 10000 * COIN; @@ -2383,7 +2348,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin REJECT_INVALID, "bad-cs-kernel"); // Check proof-of-stake min confirmations - if (pindex->nHeight - coins->nHeight < chainparams.GetConsensus().nStakeMinConfirmations) + if (pindex->nHeight - coins->nHeight < chainparams.GetConsensus().nCoinbaseMaturity) return state.DoS(100, error("ConnectBlock(): tried to stake at depth %d", pindex->nHeight - coins->nHeight), REJECT_INVALID, "bad-cs-premature"); diff --git a/src/main.h b/src/main.h index fc0353df9..6ea2fc6b5 100644 --- a/src/main.h +++ b/src/main.h @@ -443,8 +443,6 @@ public: bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& messageStart); bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); -bool ReadFromDisk(CTransaction& tx, CDiskTxPos& txindex, CBlockTreeDB& txdb, COutPoint prevout); -bool ReadFromDisk(CTransaction& tx, CDiskTxPos& txindex); /** Functions for validating blocks and updating the block tree */ diff --git a/src/pos.cpp b/src/pos.cpp index 50d048f3a..827ac2c51 100644 --- a/src/pos.cpp +++ b/src/pos.cpp @@ -117,20 +117,6 @@ bool CheckStakeKernelHash(const CBlockIndex* pindexPrev, unsigned int nBits, con return true; } -bool IsConfirmedInNPrevBlocks(const CDiskTxPos& txindex, const CBlockIndex* pindexFrom, int nMaxDepth, int& nActualDepth) -{ - for (const CBlockIndex* pindex = pindexFrom; pindex && pindexFrom->nHeight - pindex->nHeight < nMaxDepth; pindex = pindex->pprev) - { - if (pindex->nDataPos == txindex.nPos && pindex->nFile == txindex.nFile) - { - nActualDepth = pindexFrom->nHeight - pindex->nHeight; - return true; - } - } - - return false; -} - // Check kernel hash target and coinstake signature bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned int nBits, CValidationState &state) { @@ -142,25 +128,25 @@ bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned // First try finding the previous transaction in database CTransaction txPrev; - CDiskTxPos txindex; + uint256 hashBlock = uint256(); + if (!GetTransaction(txin.prevout.hash, txPrev, Params().GetConsensus(), hashBlock, true)) + return state.DoS(100, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download - if (!ReadFromDisk(txPrev, txindex, *pblocktree, txin.prevout)) - return state.DoS(1, error("CheckProofOfStake() : INFO: read txPrev failed")); // previous transaction not in main chain, may occur during initial download + if (mapBlockIndex.count(hashBlock) == 0) + return fDebug ? state.DoS(100, error("CheckProofOfStake() : read block failed")) : false; // unable to read block of previous transaction + + // Verify inputs + if (txin.prevout.hash != txPrev.GetHash()) + return state.DoS(100, error("CheckProofOfStake() : coinstake input does not match previous output %s", txin.prevout.hash.GetHex())); // Verify signature if (!VerifySignature(txPrev, tx, 0, SCRIPT_VERIFY_NONE, 0)) return state.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString())); - // Read block header - CBlock block; - const CDiskBlockPos& pos = CDiskBlockPos(txindex.nFile, txindex.nPos); - if (!ReadBlockFromDisk(block, pos, Params().GetConsensus())) - return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction - // Min age requirement - int nDepth; - if (IsConfirmedInNPrevBlocks(txindex, pindexPrev, Params().GetConsensus().nStakeMinConfirmations - 1, nDepth)) - return state.DoS(100, error("CheckProofOfStake() : tried to stake at depth %d", nDepth + 1)); + if (pindexPrev->nHeight + 1 - mapBlockIndex[hashBlock]->nHeight < Params().GetConsensus().nCoinbaseMaturity){ + return state.DoS(100, error("CheckProofOfStake() : stake prevout is not mature, expecting %i and only matured to %i", Params().GetConsensus().nCoinbaseMaturity, pindexPrev->nHeight + 1 - mapBlockIndex[hashBlock]->nHeight)); + } if (!CheckStakeKernelHash(pindexPrev, nBits, new CCoins(txPrev, pindexPrev->nHeight), txin.prevout, tx.nTime, fDebug)) return state.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s", tx.GetHash().ToString())); // may occur during initial download or if behind on block chain sync @@ -182,60 +168,67 @@ bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsig return VerifyScript(txin.scriptSig, txout.scriptPubKey, flags, TransactionSignatureChecker(&txTo, nIn, 0), NULL); } -bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, uint32_t* pBlockTime){ +bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout){ std::map tmp; - return CheckKernel(pindexPrev, nBits, nTimeBlock, prevout, pBlockTime, tmp); + return CheckKernel(pindexPrev, nBits, nTimeBlock, prevout, tmp); } -bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout, uint32_t* pBlockTime, const std::map& cache) +bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout, const std::map& cache) { uint256 hashProofOfStake, targetProofOfStake; auto it=cache.find(prevout); if(it == cache.end()) { CTransaction txPrev; - CDiskTxPos txindex; - if (!ReadFromDisk(txPrev, txindex, *pblocktree, prevout)) + uint256 hashBlock = uint256(); + if (!GetTransaction(prevout.hash, txPrev, Params().GetConsensus(), hashBlock, true)){ + LogPrintf("CheckKernel() : could not find previous transaction %s\n", prevout.hash.ToString()); return false; + } - // Read block header - CBlock block; - const CDiskBlockPos& pos = CDiskBlockPos(txindex.nFile, txindex.nPos); - if (!ReadBlockFromDisk(block, pos, Params().GetConsensus())) + if (mapBlockIndex.count(hashBlock) == 0) { + LogPrintf("CheckKernel() : could not find block of previous transaction %s\n", hashBlock.ToString()); return false; + } - int nDepth; - if (IsConfirmedInNPrevBlocks(txindex, pindexPrev, Params().GetConsensus().nStakeMinConfirmations - 1, nDepth)) + if (pindexPrev->nHeight + 1 - mapBlockIndex[hashBlock]->nHeight < Params().GetConsensus().nCoinbaseMaturity){ + LogPrintf("CheckKernel() : stake prevout is not mature in block %s\n", hashBlock.ToString()); return false; - - if (pBlockTime) - *pBlockTime = block.GetBlockTime(); + } return CheckStakeKernelHash(pindexPrev, nBits, new CCoins(txPrev, pindexPrev->nHeight), prevout, nTime); } else { //found in cache const CStakeCache& stake = it->second; - if (pBlockTime) - *pBlockTime = stake.blockFrom.GetBlockTime(); - return CheckStakeKernelHash(pindexPrev, nBits, new CCoins(stake.txPrev, pindexPrev->nHeight), prevout, nTime); + if (CheckStakeKernelHash(pindexPrev, nBits, new CCoins(stake.txPrev, pindexPrev->nHeight), prevout, nTime)) { + // Cache could potentially cause false positive stakes in the event of deep reorgs, so check without cache also + return CheckKernel(pindexPrev, nBits, nTime, prevout); + } } - } -void CacheKernel(std::map& cache, const COutPoint& prevout){ +void CacheKernel(std::map& cache, const COutPoint& prevout, CBlockIndex* pindexPrev){ if(cache.find(prevout) != cache.end()){ //already in cache return; } CTransaction txPrev; - CDiskTxPos txindex; - if (!ReadFromDisk(txPrev, txindex, *pblocktree, prevout)) + uint256 hashBlock = uint256(); + if (!GetTransaction(prevout.hash, txPrev, Params().GetConsensus(), hashBlock, true)){ + LogPrintf("CacheKernel() : could not find previous transaction %s\n", prevout.hash.ToString()); return; - // Read block - CBlock block; - const CDiskBlockPos& pos = CDiskBlockPos(txindex.nFile, txindex.nPos); - if (!ReadBlockFromDisk(block, pos, Params().GetConsensus())) + } + + if (mapBlockIndex.count(hashBlock) == 0) { + LogPrintf("CacheKernel() : could not find block of previous transaction %s\n", hashBlock.ToString()); return; - CStakeCache c(block, txindex, txPrev); + } + + if (pindexPrev->nHeight + 1 - mapBlockIndex[hashBlock]->nHeight < Params().GetConsensus().nCoinbaseMaturity){ + LogPrintf("CheckKernel() : stake prevout is not mature in block %s\n", hashBlock.ToString()); + return; + } + + CStakeCache c(hashBlock, txPrev); cache.insert({prevout, c}); } diff --git a/src/pos.h b/src/pos.h index 653a304d2..31860d1bc 100644 --- a/src/pos.h +++ b/src/pos.h @@ -26,21 +26,19 @@ using namespace std; uint256 ComputeStakeModifier(const CBlockIndex* pindexPrev, const uint256& kernel); struct CStakeCache{ - CStakeCache(CBlockHeader blockFrom_, CDiskTxPos txindex_, const CTransaction txPrev_) : blockFrom(blockFrom_), txindex(txindex_), txPrev(txPrev_){ + CStakeCache(uint256 hashBlock_, const CTransaction txPrev_) : hashBlock(hashBlock_), txPrev(txPrev_){ } - CBlockHeader blockFrom; - CDiskTxPos txindex; + uint256 hashBlock; const CTransaction txPrev; }; // Check whether the coinstake timestamp meets protocol bool CheckCoinStakeTimestamp(int64_t nTimeBlock, int64_t nTimeTx); bool CheckStakeBlockTimestamp(int64_t nTimeBlock); -bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout, uint32_t* pBlockTime = NULL); -bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout, uint32_t* pBlockTime, const std::map& cache); +bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout); +bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout, const std::map& cache); bool CheckStakeKernelHash(const CBlockIndex* pindexPrev, unsigned int nBits, const CCoins* txPrev, const COutPoint& prevout, unsigned int nTimeTx, bool fPrintProofOfStake = false); -bool IsConfirmedInNPrevBlocks(const CDiskTxPos& txindex, const CBlockIndex* pindexFrom, int nMaxDepth, int& nActualDepth); bool CheckProofOfStake(CBlockIndex* pindexPrev, const CTransaction& tx, unsigned int nBits, CValidationState &state); -void CacheKernel(std::map& cache, const COutPoint& prevout); +void CacheKernel(std::map& cache, const COutPoint& prevout, CBlockIndex* pindexPrev); bool VerifySignature(const CTransaction& txFrom, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); #endif // BLACKCOIN_POS_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 40551916a..c701d5274 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -19,7 +19,6 @@ #include "main.h" #include "net.h" #include "policy/policy.h" -#include "pos.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "script/script.h" @@ -599,12 +598,12 @@ void CWallet::AvailableCoinsForStaking(std::vector& vCoins) const { const uint256& wtxid = it->first; const CWalletTx* pcoin = &(*it).second; - int nDepth = pcoin->GetDepthInMainChain(); + if (nDepth < 1) continue; - if (nDepth < Params().GetConsensus().nStakeMinConfirmations) + if (nDepth < Params().GetConsensus().nCoinbaseMaturity) continue; if (pcoin->GetBlocksToMaturity() > 0) @@ -614,10 +613,10 @@ void CWallet::AvailableCoinsForStaking(std::vector& vCoins) const isminetype mine = IsMine(pcoin->vout[i]); if (!(IsSpent(wtxid, i)) && mine != ISMINE_NO && !IsLockedCoin((*it).first, i) && (pcoin->vout[i].nValue > 0)) - vCoins.push_back(COutput(pcoin, i, nDepth, - ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || - (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO, - (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); + vCoins.push_back(COutput(pcoin, i, nDepth, + ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || + (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO, + (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO)); } } } @@ -704,18 +703,18 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int if (setCoins.empty()) return false; - static std::map stakeCache; - if(stakeCache.size() > setCoins.size() + 100){ + if (stakeCache.size() > setCoins.size() + 100){ //Determining if the cache is still valid is harder than just clearing it when it gets too big, so instead just clear it //when it has more than 100 entries more than the actual setCoins. stakeCache.clear(); } - if(GetBoolArg("-stakecache", DEFAULT_STAKE_CACHE)) { + + if (GetBoolArg("-stakecache", DEFAULT_STAKE_CACHE)) { BOOST_FOREACH(const PAIRTYPE(const CWalletTx*, unsigned int)& pcoin, setCoins) { boost::this_thread::interruption_point(); COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); - CacheKernel(stakeCache, prevoutStake); //this will do a 2 disk loads per op + CacheKernel(stakeCache, prevoutStake, pindexPrev); //this will do a 2 disk loads per op } } @@ -732,9 +731,7 @@ bool CWallet::CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int // Search backward in time from the given txNew timestamp // Search nSearchInterval seconds back up to nMaxStakeSearchInterval COutPoint prevoutStake = COutPoint(pcoin.first->GetHash(), pcoin.second); - uint32_t nBlockTime; - //LogPrintf("looking for coinstake \n"); - if (CheckKernel(pindexPrev, nBits, txNew.nTime - n, prevoutStake, &nBlockTime, stakeCache)) + if (CheckKernel(pindexPrev, nBits, txNew.nTime - n, prevoutStake, stakeCache)) { // Found a kernel LogPrint("coinstake", "CreateCoinStake : kernel found\n"); @@ -3564,7 +3561,7 @@ uint64_t CWallet::GetStakeWeight() const LOCK2(cs_main, cs_wallet); BOOST_FOREACH(PAIRTYPE(const CWalletTx*, unsigned int) pcoin, setCoins) { - if (pcoin.first->GetDepthInMainChain() >= Params().GetConsensus().nStakeMinConfirmations) + if (pcoin.first->GetDepthInMainChain() >= Params().GetConsensus().nCoinbaseMaturity) nWeight += pcoin.first->vout[pcoin.second].nValue; } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index a0385cdf5..f5bf5016e 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -16,6 +16,7 @@ #include "wallet/crypter.h" #include "wallet/walletdb.h" #include "wallet/rpcwallet.h" +#include "pos.h" #include #include @@ -575,6 +576,8 @@ private: int64_t nLastResend; bool fBroadcastTransactions; + std::map stakeCache; + /** * Used to keep track of spent outpoints, and * detect and report conflicts (double-spends or