PoS: get rid of nStakeMinConfirmations, IsConfirmedInNPrevBlocks(), ReadFromDisk()
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -73,7 +73,6 @@ struct Params {
|
||||
int nLastPOWBlock;
|
||||
int nStakeTimestampMask;
|
||||
int nCoinbaseMaturity;
|
||||
int nStakeMinConfirmations;
|
||||
unsigned int nStakeMinAge;
|
||||
uint256 nMinimumChainWork;
|
||||
};
|
||||
|
||||
37
src/main.cpp
37
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");
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
97
src/pos.cpp
97
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<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});
|
||||
}
|
||||
|
||||
12
src/pos.h
12
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<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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user