PoS: get rid of nStakeMinConfirmations, IsConfirmedInNPrevBlocks(), ReadFromDisk()

This commit is contained in:
lateminer
2019-03-19 23:54:30 +03:00
parent 87e1ea76c9
commit 61362bef05
8 changed files with 66 additions and 116 deletions

View File

@@ -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;

View File

@@ -73,7 +73,6 @@ struct Params {
int nLastPOWBlock;
int nStakeTimestampMask;
int nCoinbaseMaturity;
int nStakeMinConfirmations;
unsigned int nStakeMinAge;
uint256 nMinimumChainWork;
};

View File

@@ -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");

View File

@@ -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 */

View File

@@ -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<COutPoint, CStakeCache> 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<COutPoint, CStakeCache>& cache)
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTime, const COutPoint& prevout, const std::map<COutPoint, CStakeCache>& 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<COutPoint, CStakeCache>& cache, const COutPoint& prevout){
void CacheKernel(std::map<COutPoint, CStakeCache>& 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});
}

View File

@@ -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<COutPoint, CStakeCache>& 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<COutPoint, CStakeCache>& 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<COutPoint, CStakeCache>& cache, const COutPoint& prevout);
void CacheKernel(std::map<COutPoint, CStakeCache>& 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

View File

@@ -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<COutput>& 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<COutput>& 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<COutPoint, CStakeCache> 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;
}

View File

@@ -16,6 +16,7 @@
#include "wallet/crypter.h"
#include "wallet/walletdb.h"
#include "wallet/rpcwallet.h"
#include "pos.h"
#include <algorithm>
#include <atomic>
@@ -575,6 +576,8 @@ private:
int64_t nLastResend;
bool fBroadcastTransactions;
std::map<COutPoint, CStakeCache> stakeCache;
/**
* Used to keep track of spent outpoints, and
* detect and report conflicts (double-spends or