diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/init.cpp | 21 | ||||
-rw-r--r-- | src/main.cpp | 72 | ||||
-rw-r--r-- | src/main.h | 7 | ||||
-rw-r--r-- | src/wallet/rpcdump.cpp | 9 |
4 files changed, 75 insertions, 34 deletions
diff --git a/src/init.cpp b/src/init.cpp index 4473cd9ec9..07f623d53c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -715,16 +715,12 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) nMaxConnections = nFD - MIN_CORE_FILEDESCRIPTORS; // if using block pruning, then disable txindex - // also disable the wallet (for now, until SPV support is implemented in wallet) if (GetArg("-prune", 0)) { if (GetBoolArg("-txindex", false)) return InitError(_("Prune mode is incompatible with -txindex.")); #ifdef ENABLE_WALLET - if (!GetBoolArg("-disablewallet", false)) { - if (SoftSetBoolArg("-disablewallet", true)) - LogPrintf("%s : parameter interaction: -prune -> setting -disablewallet=1\n", __func__); - else - return InitError(_("Can't run with a wallet in prune mode.")); + if (GetBoolArg("-rescan", false)) { + return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again.")); } #endif } @@ -1311,6 +1307,19 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) } if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { + //We can't rescan beyond non-pruned blocks, stop and throw an error + //this might happen if a user uses a old wallet within a pruned node + // or if he ran -disablewallet for a longer time, then decided to re-enable + if (fPruneMode) + { + CBlockIndex *block = chainActive.Tip(); + while (block && block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA) && block->pprev->nTx > 0 && pindexRescan != block) + block = block->pprev; + + if (pindexRescan != block) + return InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)")); + } + uiInterface.InitMessage(_("Rescanning...")); LogPrintf("Rescanning last %i blocks (from block %i)...\n", chainActive.Height() - pindexRescan->nHeight, pindexRescan->nHeight); nStart = GetTimeMillis(); diff --git a/src/main.cpp b/src/main.cpp index e92f64413a..cc9d277907 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1395,22 +1395,21 @@ bool CScriptCheck::operator()() { return true; } -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks) +int GetSpendHeight(const CCoinsViewCache& inputs) { - if (!tx.IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(tx.vin.size()); + LOCK(cs_main); + CBlockIndex* pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; + return pindexPrev->nHeight + 1; +} +namespace Consensus { +bool CheckTxInputs(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight) +{ // This doesn't trigger the DoS code on purpose; if it did, it would make it easier // for an attacker to attempt to split the network. if (!inputs.HaveInputs(tx)) return state.Invalid(error("CheckInputs(): %s inputs unavailable", tx.GetHash().ToString())); - // While checking, GetBestBlock() refers to the parent block. - // This is also true for mempool checks. - CBlockIndex *pindexPrev = mapBlockIndex.find(inputs.GetBestBlock())->second; - int nSpendHeight = pindexPrev->nHeight + 1; CAmount nValueIn = 0; CAmount nFees = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) @@ -1449,6 +1448,19 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi if (!MoneyRange(nFees)) return state.DoS(100, error("CheckInputs(): nFees out of range"), REJECT_INVALID, "bad-txns-fee-outofrange"); + return true; +} +}// namespace Consensus + +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheStore, std::vector<CScriptCheck> *pvChecks) +{ + if (!tx.IsCoinBase()) + { + if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs))) + return false; + + if (pvChecks) + pvChecks->reserve(tx.vin.size()); // The first loop above does all the inexpensive checks. // Only if ALL inputs pass do we perform expensive ECDSA signature checks. @@ -2719,18 +2731,23 @@ bool CheckBlock(const CBlock& block, CValidationState& state, bool fCheckPOW, bo return true; } -bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +static bool CheckIndexAgainstCheckpoint(const CBlockIndex* pindexPrev, CValidationState& state, const CChainParams& chainparams, const uint256& hash) { - const CChainParams& chainParams = Params(); - const Consensus::Params& consensusParams = chainParams.GetConsensus(); - uint256 hash = block.GetHash(); - if (hash == consensusParams.hashGenesisBlock) + if (*pindexPrev->phashBlock == chainparams.GetConsensus().hashGenesisBlock) return true; - assert(pindexPrev); - int nHeight = pindexPrev->nHeight+1; + // Don't accept any forks from the main chain prior to last checkpoint + CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainparams.Checkpoints()); + if (pcheckpoint && nHeight < pcheckpoint->nHeight) + return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); + + return true; +} +bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, CBlockIndex * const pindexPrev) +{ + const Consensus::Params& consensusParams = Params().GetConsensus(); // Check proof of work if (block.nBits != GetNextWorkRequired(pindexPrev, &block, consensusParams)) return state.DoS(100, error("%s: incorrect proof of work", __func__), @@ -2741,14 +2758,6 @@ bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& sta return state.Invalid(error("%s: block's timestamp is too early", __func__), REJECT_INVALID, "time-too-old"); - if (fCheckpointsEnabled) - { - // Don't accept any forks from the main chain prior to last checkpoint - CBlockIndex* pcheckpoint = Checkpoints::GetLastCheckpoint(chainParams.Checkpoints()); - if (pcheckpoint && nHeight < pcheckpoint->nHeight) - return state.DoS(100, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight)); - } - // Reject block.nVersion=1 blocks when 95% (75% on testnet) of the network has upgraded: if (block.nVersion < 2 && IsSuperMajority(2, pindexPrev, consensusParams.nMajorityRejectBlockOutdated, consensusParams)) return state.Invalid(error("%s: rejected nVersion=1 block", __func__), @@ -2818,6 +2827,9 @@ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, CBloc if (pindexPrev->nStatus & BLOCK_FAILED_MASK) return state.DoS(100, error("%s: prev block invalid", __func__), REJECT_INVALID, "bad-prevblk"); } + assert(pindexPrev); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, hash)) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); if (!ContextualCheckBlockHeader(block, state, pindexPrev)) return false; @@ -2933,8 +2945,11 @@ bool ProcessNewBlock(CValidationState &state, CNode* pfrom, CBlock* pblock, bool bool TestBlockValidity(CValidationState &state, const CBlock& block, CBlockIndex * const pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) { + const CChainParams& chainparams = Params(); AssertLockHeld(cs_main); - assert(pindexPrev == chainActive.Tip()); + assert(pindexPrev && pindexPrev == chainActive.Tip()); + if (fCheckpointsEnabled && !CheckIndexAgainstCheckpoint(pindexPrev, state, chainparams, block.GetHash())) + return error("%s: CheckIndexAgainstCheckpoint(): %s", __func__, state.GetRejectReason().c_str()); CCoinsViewCache viewNew(pcoinsTip); CBlockIndex indexDummy(block); @@ -4322,7 +4337,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, mempool.check(pcoinsTip); RelayTransaction(tx); vWorkQueue.push_back(inv.hash); - vEraseQueue.push_back(inv.hash); LogPrint("mempool", "AcceptToMemoryPool: peer=%d %s: accepted %s (poolsz %u)\n", pfrom->id, pfrom->cleanSubVer, @@ -4349,7 +4363,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, // anyone relaying LegitTxX banned) CValidationState stateDummy; - vEraseQueue.push_back(orphanHash); if (setMisbehaving.count(fromPeer)) continue; @@ -4358,6 +4371,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, LogPrint("mempool", " accepted orphan tx %s\n", orphanHash.ToString()); RelayTransaction(orphanTx); vWorkQueue.push_back(orphanHash); + vEraseQueue.push_back(orphanHash); } else if (!fMissingInputs2) { @@ -4369,8 +4383,10 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, setMisbehaving.insert(fromPeer); LogPrint("mempool", " invalid orphan tx %s\n", orphanHash.ToString()); } - // too-little-fee orphan + // Has inputs but not accepted to mempool + // Probably non-standard or insufficient fee/priority LogPrint("mempool", " removed orphan tx %s\n", orphanHash.ToString()); + vEraseQueue.push_back(orphanHash); } mempool.check(pcoinsTip); } diff --git a/src/main.h b/src/main.h index abaedae207..7eaf58d716 100644 --- a/src/main.h +++ b/src/main.h @@ -487,4 +487,11 @@ extern CCoinsViewCache *pcoinsTip; /** Global variable that points to the active block tree (protected by cs_main) */ extern CBlockTreeDB *pblocktree; +/** + * Return the spend height, which is one more than the inputs.GetBestBlock(). + * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) + * This is also true for mempool checks. + */ +int GetSpendHeight(const CCoinsViewCache& inputs); + #endif // BITCOIN_MAIN_H diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index b682a42e23..5f800474a0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -94,6 +94,9 @@ UniValue importprivkey(const UniValue& params, bool fHelp) + HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") ); + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing keys is disabled in pruned mode"); + LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); @@ -166,6 +169,9 @@ UniValue importaddress(const UniValue& params, bool fHelp) + HelpExampleRpc("importaddress", "\"myaddress\", \"testing\", false") ); + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing addresses is disabled in pruned mode"); + LOCK2(cs_main, pwalletMain->cs_wallet); CScript script; @@ -236,6 +242,9 @@ UniValue importwallet(const UniValue& params, bool fHelp) + HelpExampleRpc("importwallet", "\"test\"") ); + if (fPruneMode) + throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode"); + LOCK2(cs_main, pwalletMain->cs_wallet); EnsureWalletIsUnlocked(); |