mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-18 13:17:55 +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
532d55d059
commit
313f73d5b2
10 changed files with 77 additions and 117 deletions
|
|
@ -2083,6 +2083,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). */
|
||||
|
|
@ -2820,7 +2843,7 @@ public:
|
|||
*
|
||||
* The block is added to connectTrace if connection succeeds.
|
||||
*/
|
||||
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
|
||||
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool, bool& fStall)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(m_mempool.cs);
|
||||
|
|
@ -2838,6 +2861,14 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
|
|||
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;
|
||||
|
|
@ -2854,29 +2885,6 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
|
|||
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.GetRejectReason() == "bad-pegin-witness") {
|
||||
//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(), state.ToString());
|
||||
}
|
||||
|
|
@ -2988,7 +2996,7 @@ void CChainState::PruneBlockIndexCandidates() {
|
|||
*
|
||||
* @returns true unless a system error occurred
|
||||
*/
|
||||
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
|
||||
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace, bool& fStall)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(m_mempool.cs);
|
||||
|
|
@ -3033,7 +3041,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
|
|||
|
||||
// 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.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
|
||||
|
|
@ -3051,6 +3059,12 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
|
|||
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 || m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) {
|
||||
// We're in a better position than we were. Return temporarily to release the lock.
|
||||
|
|
@ -3130,6 +3144,8 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
|
|||
CBlockIndex *pindexMostWork = nullptr;
|
||||
CBlockIndex *pindexNewTip = nullptr;
|
||||
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
|
||||
bool fStall = false;
|
||||
|
||||
do {
|
||||
// Block until the validation queue drains. This should largely
|
||||
// never happen in normal operation, however may happen during
|
||||
|
|
@ -3160,7 +3176,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
|
|||
|
||||
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)) {
|
||||
// A system error occurred
|
||||
return false;
|
||||
}
|
||||
|
|
@ -3176,6 +3192,11 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
|
|||
assert(trace.pblock && trace.pindex);
|
||||
GetMainSignals().BlockConnected(trace.pblock, trace.pindex);
|
||||
}
|
||||
|
||||
if (fStall) {
|
||||
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
|
||||
break;
|
||||
}
|
||||
} while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));
|
||||
if (!blocks_connected) return true;
|
||||
|
||||
|
|
@ -3194,6 +3215,11 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
|
|||
}
|
||||
// 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
|
||||
|
|
@ -5711,35 +5737,12 @@ void ChainstateManager::MaybeRebalanceCaches()
|
|||
}
|
||||
|
||||
// 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);
|
||||
ChainstateManager& chainman = g_chainman;
|
||||
if (chainman.BlockIndex().count(blockhash)) {
|
||||
CBlockIndex* pblockindex = chainman.BlockIndex()[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) {
|
||||
|
|
@ -5790,50 +5793,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) {
|
||||
BlockValidationState state;
|
||||
for(const uint256& blockhash : vblocksToReconsider) {
|
||||
{
|
||||
LOCK(cs_main);
|
||||
ChainstateManager& chainman = g_chainman;
|
||||
if (chainman.BlockIndex().count(blockhash) == 0)
|
||||
continue;
|
||||
CBlockIndex* pblockindex = chainman.BlockIndex()[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);
|
||||
ChainstateManager& chainman = g_chainman;
|
||||
if (chainman.BlockIndex().count(blockhash)) {
|
||||
CBlockIndex* pblockindex = chainman.BlockIndex()[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