mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
When validation is waiting for parent chain daemon, "stall".
Currently, if -validatepegin is given, and block validation can't proceed because the parent chain is not synced, we mark the block invalid and put it in a queue to be "revalidated" later. Unfortunately, marking a block invalid has downstream consequences, in particular causing descendant blocks to be marked invalid, which are not currently fixed by the queue. Instead, we'll use a different strategy: if the mainchain daemon isn't sufficiently synced to validate a block, we will "stall" connecting that block to the chain, and have ActivateBestChain simply keep the tip at the previous block until we're ready. We can still download and validate (partly) blocks past this point while we're waiting. They will be connected once the parent chain daemon catches up.
This commit is contained in:
parent
5b1f3cc00b
commit
4ba4e0716d
8 changed files with 78 additions and 113 deletions
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
#include <future>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
|
@ -193,8 +194,8 @@ public:
|
|||
void UnloadBlockIndex();
|
||||
|
||||
private:
|
||||
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
bool ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace, bool& fStall) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
bool ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool, bool& fStall) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
|
||||
CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
|
||||
/** Create a new block index entry for a given block hash */
|
||||
|
|
@ -1936,6 +1937,29 @@ static int64_t nTimeCallbacks = 0;
|
|||
static int64_t nTimeTotal = 0;
|
||||
static int64_t nBlocksTotal = 0;
|
||||
|
||||
bool CheckPeginRipeness(const CBlock& block, const std::vector<std::pair<CScript, CScript>>& fedpegscripts) {
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++) {
|
||||
const CTransaction &tx = *(block.vtx[i]);
|
||||
|
||||
if (!tx.IsCoinBase()) {
|
||||
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
|
||||
if (tx.vin[i].m_is_pegin) {
|
||||
std::string err;
|
||||
bool depth_failed = false;
|
||||
if ((tx.witness.vtxinwit.size() <= i) || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, tx.vin[i].prevout, err, true, &depth_failed)) {
|
||||
if (depth_failed) {
|
||||
return false; // Pegins not ripe.
|
||||
} else {
|
||||
return true; // Some other failure; details later.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
|
||||
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
|
||||
* can fail if those validity checks fail (among other reasons). */
|
||||
|
|
@ -2601,7 +2625,7 @@ public:
|
|||
*
|
||||
* The block is added to connectTrace if connection succeeds.
|
||||
*/
|
||||
bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
|
||||
bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool, bool& fStall)
|
||||
{
|
||||
assert(pindexNew->pprev == chainActive.Tip());
|
||||
// Read block from disk.
|
||||
|
|
@ -2616,6 +2640,14 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
|
|||
pthisBlock = pblock;
|
||||
}
|
||||
const CBlock& blockConnecting = *pthisBlock;
|
||||
|
||||
const auto& fedpegscripts = GetValidFedpegScripts(pindexNew, chainparams.GetConsensus(), false /* nextblock_validation */);
|
||||
if (!CheckPeginRipeness(blockConnecting, fedpegscripts)) {
|
||||
LogPrintf("STALLING further progress in ConnectTip while waiting for parent chain daemon to catch up! Chain will not grow until this is remedied!\n");
|
||||
fStall = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply the block atomically to the chain state.
|
||||
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
|
||||
int64_t nTime3;
|
||||
|
|
@ -2632,29 +2664,6 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
|
|||
if (!rv) {
|
||||
if (state.IsInvalid()) {
|
||||
InvalidBlockFound(pindexNew, state);
|
||||
|
||||
// ELEMENTS:
|
||||
// Possibly result of RPC to mainchain bitcoind failure
|
||||
// or unseen Bitcoin blocks.
|
||||
// These blocks are later re-evaluated at an interval
|
||||
// set by `-recheckpeginblockinterval`.
|
||||
if (state.GetRejectCode() == REJECT_PEGIN) {
|
||||
//Write queue of invalid blocks that
|
||||
//must be cleared to continue operation
|
||||
std::vector<uint256> vinvalidBlocks;
|
||||
pblocktree->ReadInvalidBlockQueue(vinvalidBlocks);
|
||||
bool blockAlreadyInvalid = false;
|
||||
for (uint256& hash : vinvalidBlocks) {
|
||||
if (hash == blockConnecting.GetHash()) {
|
||||
blockAlreadyInvalid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!blockAlreadyInvalid) {
|
||||
vinvalidBlocks.push_back(blockConnecting.GetHash());
|
||||
pblocktree->WriteInvalidBlockQueue(vinvalidBlocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), FormatStateMessage(state));
|
||||
}
|
||||
|
|
@ -2762,7 +2771,7 @@ void CChainState::PruneBlockIndexCandidates() {
|
|||
* Try to make some progress towards making pindexMostWork the active block.
|
||||
* pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork.
|
||||
*/
|
||||
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
|
||||
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace, bool& fStall)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
|
||||
|
|
@ -2801,7 +2810,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
|
|||
|
||||
// Connect new blocks.
|
||||
for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) {
|
||||
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
|
||||
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool, fStall)) {
|
||||
if (state.IsInvalid()) {
|
||||
// The block violates a consensus rule.
|
||||
if (!state.CorruptionPossible()) {
|
||||
|
|
@ -2819,6 +2828,12 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
if (fStall) {
|
||||
// We didn't make progress because the parent chain is not
|
||||
// synced enough to check pegins. Try again later.
|
||||
fContinue = false;
|
||||
break;
|
||||
}
|
||||
PruneBlockIndexCandidates();
|
||||
if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
|
||||
// We're in a better position than we were. Return temporarily to release the lock.
|
||||
|
|
@ -2899,6 +2914,8 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
|
|||
CBlockIndex *pindexMostWork = nullptr;
|
||||
CBlockIndex *pindexNewTip = nullptr;
|
||||
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
|
||||
bool fStall = false;
|
||||
|
||||
do {
|
||||
boost::this_thread::interruption_point();
|
||||
|
||||
|
|
@ -2930,7 +2947,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
|
|||
|
||||
bool fInvalidFound = false;
|
||||
std::shared_ptr<const CBlock> nullBlockPtr;
|
||||
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
|
||||
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace, fStall))
|
||||
return false;
|
||||
blocks_connected = true;
|
||||
|
||||
|
|
@ -2944,6 +2961,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
|
|||
assert(trace.pblock && trace.pindex);
|
||||
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
|
||||
}
|
||||
|
||||
if (fStall) {
|
||||
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
|
||||
break;
|
||||
}
|
||||
} while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
|
||||
if (!blocks_connected) return true;
|
||||
|
||||
|
|
@ -2962,6 +2984,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
|
|||
}
|
||||
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
|
||||
|
||||
if (fStall) {
|
||||
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
|
||||
break;
|
||||
}
|
||||
|
||||
if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
|
||||
|
||||
// We check shutdown only after giving ActivateBestChainStep a chance to run once so that we
|
||||
|
|
@ -5306,34 +5333,12 @@ public:
|
|||
} instance_of_cmaincleanup;
|
||||
|
||||
// ELEMENTS:
|
||||
/* This function has two major purposes:
|
||||
* 1) Checks that the RPC connection to the parent chain node
|
||||
/* This function checks that the RPC connection to the parent chain node
|
||||
* can be attained, and is returning back reasonable answers.
|
||||
* 2) Re-evaluates a list of blocks that have been deemed "bad"
|
||||
* from the perspective of peg-in witness validation. Blocks are
|
||||
* added to this queue in ConnectTip based on the error code returned.
|
||||
*/
|
||||
bool MainchainRPCCheck(const bool init)
|
||||
{
|
||||
// First, we can clear out any blocks thatsomehow are now deemed valid
|
||||
// eg reconsiderblock rpc call manually
|
||||
std::vector<uint256> vblocksToReconsider;
|
||||
pblocktree->ReadInvalidBlockQueue(vblocksToReconsider);
|
||||
std::vector<uint256> vblocksToReconsiderAgain;
|
||||
for(uint256& blockhash : vblocksToReconsider) {
|
||||
LOCK(cs_main);
|
||||
if (mapBlockIndex.count(blockhash)) {
|
||||
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
|
||||
if ((pblockindex->nStatus & BLOCK_FAILED_MASK)) {
|
||||
vblocksToReconsiderAgain.push_back(blockhash);
|
||||
}
|
||||
}
|
||||
}
|
||||
vblocksToReconsider = vblocksToReconsiderAgain;
|
||||
vblocksToReconsiderAgain.clear();
|
||||
pblocktree->WriteInvalidBlockQueue(vblocksToReconsider);
|
||||
|
||||
// Next, check for working and valid rpc
|
||||
// Check for working and valid rpc
|
||||
if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) {
|
||||
// During init try until a non-RPC_IN_WARMUP result
|
||||
while (true) {
|
||||
|
|
@ -5384,48 +5389,5 @@ bool MainchainRPCCheck(const bool init)
|
|||
}
|
||||
}
|
||||
|
||||
//Sanity startup check won't reconsider queued blocks
|
||||
if (init) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Getting this far means we either aren't validating pegins(so let's make sure that's why
|
||||
// it failed previously) or we successfully connected to bitcoind
|
||||
// Time to reconsider blocks
|
||||
if (vblocksToReconsider.size() > 0) {
|
||||
CValidationState state;
|
||||
for(const uint256& blockhash : vblocksToReconsider) {
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (mapBlockIndex.count(blockhash) == 0)
|
||||
continue;
|
||||
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
|
||||
ResetBlockFailureFlags(pblockindex);
|
||||
}
|
||||
}
|
||||
|
||||
//All blocks are now being reconsidered
|
||||
ActivateBestChain(state, Params());
|
||||
//This simply checks for DB errors
|
||||
if (!state.IsValid()) {
|
||||
//Something scary?
|
||||
}
|
||||
|
||||
//Now to clear out now-valid blocks
|
||||
for(const uint256& blockhash : vblocksToReconsider) {
|
||||
LOCK(cs_main);
|
||||
if (mapBlockIndex.count(blockhash)) {
|
||||
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
|
||||
|
||||
//Marked as invalid still, put back into queue
|
||||
if((pblockindex->nStatus & BLOCK_FAILED_MASK)) {
|
||||
vblocksToReconsiderAgain.push_back(blockhash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Write back remaining blocks
|
||||
pblocktree->WriteInvalidBlockQueue(vblocksToReconsiderAgain);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue