Skip to content

Commit

Permalink
Merge pull request bitcoin#334 from qtumproject/earlz/remove-txindex-dep
Browse files Browse the repository at this point in the history
Remove dependency on txindex for checking PoS blocks
  • Loading branch information
qtum-neil authored Sep 7, 2017
2 parents d344a37 + c2ddc52 commit b9ed2a6
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 184 deletions.
32 changes: 1 addition & 31 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,24 +354,16 @@ std::string HelpMessage(HelpMessageMode mode)
#ifndef WIN32
strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file (default: %s)"), BITCOIN_PID_FILENAME));
#endif
#if 0
// *** The Qtum wallet currently requires txindex to be set/true. Therefore pruning is not allowed.
// *** TODO: Add support for pruning (while still maintaining txindex).
strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. "
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024));
#endif
strUsage += HelpMessageOpt("-record-log-opcodes", strprintf(_("Logs all EVM LOG opcode operations to the file vmExecLogs.json")));
strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks"));
strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk"));
#ifndef WIN32
strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)"));
#endif
#if 0
// *** The Qtum wallet currently requires txindex to be set/true.
// *** TODO: Add support for pruning (while still maintaining txindex).
strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX));
#endif
strUsage += HelpMessageOpt("-logevents", strprintf(_("Maintain a full EVM log index, used by searchlogs and gettransactionreceipt rpc calls (default: %u)"), DEFAULT_LOGEVENTS));

strUsage += HelpMessageGroup(_("Connection options:"));
Expand Down Expand Up @@ -451,13 +443,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT));
strUsage += HelpMessageOpt("-bip9params=deployment:start:end", "Use given start/end times for specified BIP9 deployment (regtest-only)");
}
#if 0
// *** The Qtum wallet currently requires txindex to be set/true. Therefore pruning is not allowed.
// *** TODO: Add support for pruning (while still maintaining txindex)
std::string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below
#else
std::string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, net, proxy, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below
#endif
std::string debugCategories = "addrman, alert, bench, cmpctblock, coindb, db, http, libevent, lock, mempool, mempoolrej, miner, net, proxy, prune, rand, reindex, rpc, selectcoins, tor, zmq"; // Don't translate these and qt below
if (mode == HMM_BITCOIN_QT)
debugCategories += ", qt";
strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " +
Expand Down Expand Up @@ -907,15 +893,11 @@ bool AppInitParameterInteraction()

// also see: InitParameterInteraction()

#if 0
// *** The Qtum wallet currently requires txindex to be set/true. Therefore pruning is not allowed.
// *** TODO: Add support for pruning (while still maintaining txindex).
// if using block pruning, then disallow txindex
if (GetArg("-prune", 0)) {
if (GetBoolArg("-txindex", DEFAULT_TXINDEX))
return InitError(_("Prune mode is incompatible with -txindex."));
}
#endif

// Make sure enough file descriptors are available
int nBind = std::max(
Expand Down Expand Up @@ -1001,14 +983,8 @@ bool AppInitParameterInteraction()
else if (nScriptCheckThreads > MAX_SCRIPTCHECK_THREADS)
nScriptCheckThreads = MAX_SCRIPTCHECK_THREADS;

#if 0
// *** The Qtum wallet currently requires txindex to be set/true. Therefore pruning is not allowed.
// *** TODO: Add support for pruning (while still maintaining txindex).
// block pruning; get the amount of disk space (in MiB) to allot for block & undo files
int64_t nPruneArg = GetArg("-prune", 0);
#else
int64_t nPruneArg = 0;
#endif
if (nPruneArg < 0) {
return InitError(_("Prune cannot be configured with a negative value."));
}
Expand Down Expand Up @@ -1455,13 +1431,7 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
nTotalCache = std::min(nTotalCache, nMaxDbCache << 20); // total cache cannot be greater than nMaxDbcache
int64_t nBlockTreeDBCache = nTotalCache / 8;
#if 0
// *** The Qtum wallet currently requires txindex to be set/true.
// *** TODO: Add support for pruning (while still maintaining txindex).
nBlockTreeDBCache = std::min(nBlockTreeDBCache, (GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxBlockDBAndTxIndexCache : nMaxBlockDBCache) << 20);
#else
nBlockTreeDBCache = std::min(nBlockTreeDBCache, nMaxBlockDBAndTxIndexCache << 20);
#endif
nTotalCache -= nBlockTreeDBCache;
int64_t nCoinDBCache = std::min(nTotalCache / 2, (nTotalCache / 4) + (1 << 23)); // use 25%-50% of the remainder for disk cache
nCoinDBCache = std::min(nCoinDBCache, nMaxCoinsDBCache << 20); // cap total coins db cache
Expand Down
2 changes: 1 addition & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1039,7 +1039,7 @@ bool CheckStake(const std::shared_ptr<const CBlock> pblock, CWallet& wallet)

// verify hash target and signature of coinstake tx
CValidationState state;
if (!CheckProofOfStake(mapBlockIndex[pblock->hashPrevBlock], state, *pblock->vtx[1], pblock->nBits, pblock->nTime, proofHash, hashTarget))
if (!CheckProofOfStake(mapBlockIndex[pblock->hashPrevBlock], state, *pblock->vtx[1], pblock->nBits, pblock->nTime, proofHash, hashTarget, *pcoinsTip))
return error("CheckStake() : proof-of-stake checking failed");

//// debug print
Expand Down
103 changes: 57 additions & 46 deletions src/pos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ uint256 ComputeStakeModifier(const CBlockIndex* pindexPrev, const uint256& kerne
// quantities so as to generate blocks faster, degrading the system back into
// a proof-of-work situation.
//
bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, CAmount prevoutValue, const COutPoint& prevout, unsigned int nTimeBlock, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake)
bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t blockFromTime, CAmount prevoutValue, const COutPoint& prevout, unsigned int nTimeBlock, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake)
{
if (nTimeBlock < blockFrom.nTime) // Transaction timestamp violation
if (nTimeBlock < blockFromTime) // Transaction timestamp violation
return error("CheckStakeKernelHash() : nTime violation");

// Base target
Expand All @@ -72,14 +72,14 @@ bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, const CBl
// Calculate hash
CDataStream ss(SER_GETHASH, 0);
ss << nStakeModifier;
ss << blockFrom.nTime << prevout.hash << prevout.n << nTimeBlock;
ss << blockFromTime << prevout.hash << prevout.n << nTimeBlock;
hashProofOfStake = Hash(ss.begin(), ss.end());

if (fPrintProofOfStake)
{
LogPrintf("CheckStakeKernelHash() : check modifier=%s nTimeBlockFrom=%u nPrevout=%u nTimeBlock=%u hashProof=%s\n",
nStakeModifier.GetHex().c_str(),
blockFrom.nTime, prevout.n, nTimeBlock,
blockFromTime, prevout.n, nTimeBlock,
hashProofOfStake.ToString());
}

Expand All @@ -91,43 +91,40 @@ bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, const CBl
{
LogPrintf("CheckStakeKernelHash() : check modifier=%s nTimeBlockFrom=%u nPrevout=%u nTimeBlock=%u hashProof=%s\n",
nStakeModifier.GetHex().c_str(),
blockFrom.nTime, prevout.n, nTimeBlock,
blockFromTime, prevout.n, nTimeBlock,
hashProofOfStake.ToString());
}

return true;
}

// Check kernel hash target and coinstake signature
bool CheckProofOfStake(CBlockIndex* pindexPrev, CValidationState& state, const CTransaction& tx, unsigned int nBits, uint32_t nTimeBlock, uint256& hashProofOfStake, uint256& targetProofOfStake)
bool CheckProofOfStake(CBlockIndex* pindexPrev, CValidationState& state, const CTransaction& tx, unsigned int nBits, uint32_t nTimeBlock, uint256& hashProofOfStake, uint256& targetProofOfStake, CCoinsViewCache& view)
{
if (!tx.IsCoinStake())
return error("CheckProofOfStake() : called on non-coinstake %s", tx.GetHash().ToString());

// Kernel (input 0) must match the stake hash target (nBits)
const CTxIn& txin = tx.vin[0];

// First try finding the previous transaction in database
CMutableTransaction txPrev;
CDiskTxPos txindex;
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
CCoins coinsPrev;
if(!view.GetCoins(txin.prevout.hash, coinsPrev)){
return state.DoS(100, error("CheckProofOfStake() : Stake prevout does not exist %s", txin.prevout.hash.ToString()));
}

if(pindexPrev->nHeight + 1 - coinsPrev.nHeight < COINBASE_MATURITY){
return state.DoS(100, error("CheckProofOfStake() : Stake prevout is not mature, expecting %i and only matured to %i", COINBASE_MATURITY, pindexPrev->nHeight + 1 - coinsPrev.nHeight));
}
CBlockIndex* blockFrom = pindexPrev->GetAncestor(coinsPrev.nHeight);
if(!blockFrom) {
return state.DoS(100, error("CheckProofOfStake() : Block at height %i for prevout can not be loaded", coinsPrev.nHeight));
}

// Verify signature
if (!VerifySignature(txPrev, tx, 0, SCRIPT_VERIFY_NONE))
if (!VerifySignature(coinsPrev, txin.prevout.hash, tx, 0, SCRIPT_VERIFY_NONE))
return state.DoS(100, error("CheckProofOfStake() : VerifySignature failed on coinstake %s", tx.GetHash().ToString()));

// Read block header
CBlockHeader blockFrom;
if (!ReadFromDisk(blockFrom, txindex.nFile, txindex.nPos))
return fDebug? error("CheckProofOfStake() : read block failed") : false; // unable to read block of previous transaction

// Min age requirement
int nDepth;
if (IsConfirmedInNPrevBlocks(txindex, pindexPrev, COINBASE_MATURITY - 1, nDepth))
return state.DoS(100, error("CheckProofOfStake() : tried to stake at depth %d", nDepth + 1));

if (!CheckStakeKernelHash(pindexPrev, nBits, blockFrom, txindex.nTxOffset - txindex.nPos, txPrev.vout[txin.prevout.n].nValue, txin.prevout, nTimeBlock, hashProofOfStake, targetProofOfStake, fDebug))
if (!CheckStakeKernelHash(pindexPrev, nBits, blockFrom->nTime, coinsPrev.vout[txin.prevout.n].nValue, txin.prevout, nTimeBlock, hashProofOfStake, targetProofOfStake, fDebug))
return state.DoS(1, error("CheckProofOfStake() : INFO: check kernel failed on coinstake %s, hashProof=%s", tx.GetHash().ToString(), hashProofOfStake.ToString())); // may occur during initial download or if behind on block chain sync

return true;
Expand All @@ -140,55 +137,68 @@ bool CheckCoinStakeTimestamp(uint32_t nTimeBlock)
}


bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout){
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, CCoinsViewCache& view)
{
std::map<COutPoint, CStakeCache> tmp;
return CheckKernel(pindexPrev, nBits, nTimeBlock, prevout, tmp);
return CheckKernel(pindexPrev, nBits, nTimeBlock, prevout, view, tmp);
}

bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, const std::map<COutPoint, CStakeCache>& cache)
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, CCoinsViewCache& view, const std::map<COutPoint, CStakeCache>& cache)
{
uint256 hashProofOfStake, targetProofOfStake;
auto it=cache.find(prevout);
if(it == cache.end()) {
//not found in cache (shouldn't happen during staking, only during verification which does not use cache)
CMutableTransaction txPrev;
CDiskTxPos txindex;
if (!ReadFromDisk(txPrev, txindex, *pblocktree, prevout))
CCoins coinsPrev;
if(!view.GetCoins(prevout.hash, coinsPrev)){
return false;
}

// Read block header
CBlockHeader block;
if (!ReadFromDisk(block, txindex.nFile, txindex.nPos))
if(pindexPrev->nHeight + 1 - coinsPrev.nHeight < COINBASE_MATURITY){
return false;

int nDepth;
if (IsConfirmedInNPrevBlocks(txindex, pindexPrev, COINBASE_MATURITY - 1, nDepth))
}
CBlockIndex* blockFrom = pindexPrev->GetAncestor(coinsPrev.nHeight);
if(!blockFrom) {
return false;
}
if(!coinsPrev.IsAvailable(prevout.n)){
return false;
}

return CheckStakeKernelHash(pindexPrev, nBits, block, txindex.nTxOffset - txindex.nPos, txPrev.vout[prevout.n].nValue, prevout,
return CheckStakeKernelHash(pindexPrev, nBits, blockFrom->nTime, coinsPrev.vout[prevout.n].nValue, prevout,
nTimeBlock, hashProofOfStake, targetProofOfStake);
}else{
//found in cache
const CStakeCache& stake = it->second;
return CheckStakeKernelHash(pindexPrev, nBits, stake.blockFrom, stake.txindex.nTxOffset - stake.txindex.nPos, stake.amount, prevout,
nTimeBlock, hashProofOfStake, targetProofOfStake);
if(CheckStakeKernelHash(pindexPrev, nBits, stake.blockFromTime, stake.amount, prevout,
nTimeBlock, hashProofOfStake, targetProofOfStake)){
//Cache could potentially cause false positive stakes in the event of deep reorgs, so check without cache also
return CheckKernel(pindexPrev, nBits, nTimeBlock, prevout, view);
}
}
return false;
}

void CacheKernel(std::map<COutPoint, CStakeCache>& cache, const COutPoint& prevout){
void CacheKernel(std::map<COutPoint, CStakeCache>& cache, const COutPoint& prevout, CBlockIndex* pindexPrev, CCoinsViewCache& view){
if(cache.find(prevout) != cache.end()){
//already in cache
return;
}
CMutableTransaction txPrev;
CDiskTxPos txindex;
if (!ReadFromDisk(txPrev, txindex, *pblocktree, prevout))

CCoins coinsPrev;
if(!view.GetCoins(prevout.hash, coinsPrev)){
return;
// Read block header
CBlockHeader block;
if (!ReadFromDisk(block, txindex.nFile, txindex.nPos))
}

if(pindexPrev->nHeight + 1 - coinsPrev.nHeight < COINBASE_MATURITY){
return;
CStakeCache c(block, txindex, txPrev.vout[prevout.n].nValue);
}
CBlockIndex* blockFrom = pindexPrev->GetAncestor(coinsPrev.nHeight);
if(!blockFrom) {
return;
}

CStakeCache c(blockFrom->nTime, coinsPrev.vout[prevout.n].nValue);
cache.insert({prevout, c});
}

Expand All @@ -212,5 +222,6 @@ void CacheKernel(std::map<COutPoint, CStakeCache>& cache, const COutPoint& prevo






15 changes: 7 additions & 8 deletions src/pos.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,32 @@
static const uint32_t STAKE_TIMESTAMP_MASK = 15;

struct CStakeCache{
CStakeCache(CBlockHeader blockFrom_, CDiskTxPos txindex_, CAmount amount_) : blockFrom(blockFrom_), txindex(txindex_), amount(amount_){
CStakeCache(uint32_t blockFromTime_, CAmount amount_) : blockFromTime(blockFromTime_), amount(amount_){
}
CBlockHeader blockFrom;
CDiskTxPos txindex;
uint32_t blockFromTime;
CAmount amount;
};

void CacheKernel(std::map<COutPoint, CStakeCache>& cache, const COutPoint& prevout);
void CacheKernel(std::map<COutPoint, CStakeCache>& cache, const COutPoint& prevout, CBlockIndex* pindexPrev, CCoinsViewCache& view);

// Compute the hash modifier for proof-of-stake
uint256 ComputeStakeModifier(const CBlockIndex* pindexPrev, const uint256& kernel);

// Check whether stake kernel meets hash target
// Sets hashProofOfStake on success return
bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, const CBlockHeader& blockFrom, unsigned int nTxPrevOffset, CAmount prevoutAmount, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake=false);
bool CheckStakeKernelHash(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t blockFromTime, CAmount prevoutAmount, const COutPoint& prevout, unsigned int nTimeTx, uint256& hashProofOfStake, uint256& targetProofOfStake, bool fPrintProofOfStake=false);

// Check kernel hash target and coinstake signature
// Sets hashProofOfStake on success return
bool CheckProofOfStake(CBlockIndex* pindexPrev, CValidationState& state, const CTransaction& tx, unsigned int nBits, uint32_t nTimeBlock, uint256& hashProofOfStake, uint256& targetProofOfStake);
bool CheckProofOfStake(CBlockIndex* pindexPrev, CValidationState& state, const CTransaction& tx, unsigned int nBits, uint32_t nTimeBlock, uint256& hashProofOfStake, uint256& targetProofOfStake, CCoinsViewCache& view);

// Check whether the coinstake timestamp meets protocol
bool CheckCoinStakeTimestamp(uint32_t nTimeBlock);

// Wrapper around CheckStakeKernelHash()
// Also checks existence of kernel input and min age
// Convenient for searching a kernel
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout);
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, const std::map<COutPoint, CStakeCache>& cache);
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, CCoinsViewCache& view);
bool CheckKernel(CBlockIndex* pindexPrev, unsigned int nBits, uint32_t nTimeBlock, const COutPoint& prevout, CCoinsViewCache& view, const std::map<COutPoint, CStakeCache>& cache);

#endif // QUANTUM_POS_H
6 changes: 0 additions & 6 deletions src/qt/intro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,7 @@ Intro::Intro(QWidget *parent) :
ui->setupUi(this);
ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME)));
ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME)));
#if 0
// *** The Qtum wallet currently requires txindex to be set/true. Therefore pruning is not allowed.
// *** TODO: Add support for pruning (while still maintaining txindex).
uint64_t pruneTarget = std::max<int64_t>(0, GetArg("-prune", 0));
#else
uint64_t pruneTarget = 0;
#endif
requiredSpace = BLOCK_CHAIN_SIZE;
if (pruneTarget) {
uint64_t prunedGBs = std::ceil(pruneTarget * 1024 * 1024.0 / GB_BYTES);
Expand Down
Loading

0 comments on commit b9ed2a6

Please sign in to comment.