Skip to content

Commit 841fd14

Browse files
committed
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.
1 parent 5b1f3cc commit 841fd14

File tree

5 files changed

+72
-109
lines changed

5 files changed

+72
-109
lines changed

src/init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,6 @@ void SetupServerArgs()
582582
gArgs.AddArg("-mainchainrpccookiefile=<file>", "The bitcoind cookie auth path which the daemon will use to connect to the trusted mainchain daemon to validate peg-ins. (default: `<datadir>/regtest/.cookie`)", false, OptionsCategory::ELEMENTS);
583583
gArgs.AddArg("-mainchainrpctimeout=<n>", strprintf("Timeout in seconds during mainchain RPC requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::ELEMENTS);
584584
gArgs.AddArg("-peginconfirmationdepth=<n>", strprintf("Pegin claims must be this deep to be considered valid. (default: %d)", DEFAULT_PEGIN_CONFIRMATION_DEPTH), false, OptionsCategory::ELEMENTS);
585-
gArgs.AddArg("-recheckpeginblockinterval=<n>", strprintf("The interval in seconds at which a peg-in witness failing block is re-evaluated in case of intermittent peg-in witness failure. 0 means never. (default: %u)", 120), false, OptionsCategory::ELEMENTS);
586585
gArgs.AddArg("-parentpubkeyprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 pubkey address. (default: %d)", 111), false, OptionsCategory::CHAINPARAMS);
587586
gArgs.AddArg("-parentscriptprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 script address. (default: %d)", 196), false, OptionsCategory::CHAINPARAMS);
588587
gArgs.AddArg("-parent_bech32_hrp", strprintf("The human-readable part of the parent chain's bech32 encoding. (default: %s)", "bc"), false, OptionsCategory::CHAINPARAMS);

src/mainchainrpc.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,18 +152,24 @@ UniValue CallMainChainRPC(const std::string& strMethod, const UniValue& params)
152152

153153
bool IsConfirmedBitcoinBlock(const uint256& hash, const int nMinConfirmationDepth, const int nbTxs)
154154
{
155+
LogPrintf("Checking for confirmed bitcoin block with hash %s, mindepth %d, nbtxs %d\n", hash.ToString().c_str(), nMinConfirmationDepth, nbTxs);
155156
try {
156157
UniValue params(UniValue::VARR);
157158
params.push_back(hash.GetHex());
158159
UniValue reply = CallMainChainRPC("getblockheader", params);
159-
if (!find_value(reply, "error").isNull())
160+
if (!find_value(reply, "error").isNull()) {
161+
LogPrintf("ERROR: Got error reply from bitcoind getblockheader.\n");
160162
return false;
163+
}
161164
UniValue result = find_value(reply, "result");
162-
if (!result.isObject())
165+
if (!result.isObject()) {
166+
LogPrintf("ERROR: bitcoind getblockheader result was malformed (not object).\n");
163167
return false;
168+
}
164169

165170
UniValue confirmations = find_value(result.get_obj(), "confirmations");
166171
if (!confirmations.isNum() || confirmations.get_int64() < nMinConfirmationDepth) {
172+
LogPrintf("Insufficient confirmations (got %s).\n", confirmations.write());
167173
return false;
168174
}
169175

src/txdb.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -272,14 +272,6 @@ bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
272272
return true;
273273
}
274274

275-
// ELEMENTS:
276-
bool CBlockTreeDB::ReadInvalidBlockQueue(std::vector<uint256> &vBlocks) {
277-
return Read(std::make_pair(DB_INVALID_BLOCK_Q, uint256S("0")), vBlocks);//FIXME: why uint 56 and not ""
278-
}
279-
bool CBlockTreeDB::WriteInvalidBlockQueue(const std::vector<uint256> &vBlocks) {
280-
return Write(std::make_pair(DB_INVALID_BLOCK_Q, uint256S("0")), vBlocks);
281-
}
282-
283275
bool CBlockTreeDB::ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject)
284276
{
285277
return Read(std::make_pair(DB_PAK, uint256S("1")), offline_list) && Read(std::make_pair(DB_PAK, uint256S("2")), online_list) && Read(std::make_pair(DB_PAK, uint256S("3")), reject);

src/txdb.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,6 @@ class CBlockTreeDB : public CDBWrapper
9999
bool ReadFlag(const std::string &name, bool &fValue);
100100
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
101101
// ELEMENTS:
102-
bool ReadInvalidBlockQueue(std::vector<uint256> &vBlocks);
103-
bool WriteInvalidBlockQueue(const std::vector<uint256> &vBlocks);
104102
bool ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject);
105103
bool WritePAKList(const std::vector<std::vector<unsigned char> >& offline_list, const std::vector<std::vector<unsigned char> >& online_list, bool reject);
106104
};

src/validation.cpp

Lines changed: 64 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747

4848
#include <future>
4949
#include <sstream>
50+
#include <string>
5051

5152
#include <boost/algorithm/string/replace.hpp>
5253
#include <boost/thread.hpp>
@@ -193,8 +194,8 @@ class CChainState {
193194
void UnloadBlockIndex();
194195

195196
private:
196-
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);
197-
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);
197+
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);
198+
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);
198199

199200
CBlockIndex* AddToBlockIndex(const CBlockHeader& block) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
200201
/** Create a new block index entry for a given block hash */
@@ -1936,6 +1937,35 @@ static int64_t nTimeCallbacks = 0;
19361937
static int64_t nTimeTotal = 0;
19371938
static int64_t nBlocksTotal = 0;
19381939

1940+
bool CheckPeginRipeness(const CBlock& block, const std::vector<std::pair<CScript, CScript>>& fedpegscripts) {
1941+
for (unsigned int i = 0; i < block.vtx.size(); i++) {
1942+
const CTransaction &tx = *(block.vtx[i]);
1943+
1944+
if (!tx.IsCoinBase()) {
1945+
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
1946+
if (tx.vin[i].m_is_pegin) {
1947+
std::string err;
1948+
// IsValidPeginWitness technically checks more than just "ripeness". As a result,
1949+
// if we get a validly-signed block which contains an ill-formed pegin witness,
1950+
// this test will fail, and it will be treated as "unripe" rather than invalid,
1951+
// resulting in a stall rather than rejecting the block. This is reasonable, because
1952+
// in this case (invalid block that is validly-signed by the federation), something
1953+
// very strange has happened anyway.
1954+
if ((tx.witness.vtxinwit.size() <= i) || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, tx.vin[i].prevout, err, true)) {
1955+
// This is not the ideal way to check this, but we already do this in one other place.
1956+
if (!strcmp(err.c_str(), "Needs more confirmations.")) {
1957+
return false; // Pegins not ripe.
1958+
} else {
1959+
return true; // Some other failure; details later.
1960+
}
1961+
}
1962+
}
1963+
}
1964+
}
1965+
}
1966+
return true;
1967+
}
1968+
19391969
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
19401970
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
19411971
* can fail if those validity checks fail (among other reasons). */
@@ -2601,7 +2631,7 @@ class ConnectTrace {
26012631
*
26022632
* The block is added to connectTrace if connection succeeds.
26032633
*/
2604-
bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
2634+
bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool, bool& fStall)
26052635
{
26062636
assert(pindexNew->pprev == chainActive.Tip());
26072637
// Read block from disk.
@@ -2616,6 +2646,14 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
26162646
pthisBlock = pblock;
26172647
}
26182648
const CBlock& blockConnecting = *pthisBlock;
2649+
2650+
const auto& fedpegscripts = GetValidFedpegScripts(pindexNew, chainparams.GetConsensus(), false /* nextblock_validation */);
2651+
if (!CheckPeginRipeness(blockConnecting, fedpegscripts)) {
2652+
LogPrintf("STALLING further progress in ConnectTip while waiting for parent chain daemon to catch up! Chain will not grow until this is remedied!\n");
2653+
fStall = true;
2654+
return true;
2655+
}
2656+
26192657
// Apply the block atomically to the chain state.
26202658
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
26212659
int64_t nTime3;
@@ -2632,29 +2670,6 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp
26322670
if (!rv) {
26332671
if (state.IsInvalid()) {
26342672
InvalidBlockFound(pindexNew, state);
2635-
2636-
// ELEMENTS:
2637-
// Possibly result of RPC to mainchain bitcoind failure
2638-
// or unseen Bitcoin blocks.
2639-
// These blocks are later re-evaluated at an interval
2640-
// set by `-recheckpeginblockinterval`.
2641-
if (state.GetRejectCode() == REJECT_PEGIN) {
2642-
//Write queue of invalid blocks that
2643-
//must be cleared to continue operation
2644-
std::vector<uint256> vinvalidBlocks;
2645-
pblocktree->ReadInvalidBlockQueue(vinvalidBlocks);
2646-
bool blockAlreadyInvalid = false;
2647-
for (uint256& hash : vinvalidBlocks) {
2648-
if (hash == blockConnecting.GetHash()) {
2649-
blockAlreadyInvalid = true;
2650-
break;
2651-
}
2652-
}
2653-
if (!blockAlreadyInvalid) {
2654-
vinvalidBlocks.push_back(blockConnecting.GetHash());
2655-
pblocktree->WriteInvalidBlockQueue(vinvalidBlocks);
2656-
}
2657-
}
26582673
}
26592674
return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), FormatStateMessage(state));
26602675
}
@@ -2762,7 +2777,7 @@ void CChainState::PruneBlockIndexCandidates() {
27622777
* Try to make some progress towards making pindexMostWork the active block.
27632778
* pblock is either nullptr or a pointer to a CBlock corresponding to pindexMostWork.
27642779
*/
2765-
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
2780+
bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace, bool& fStall)
27662781
{
27672782
AssertLockHeld(cs_main);
27682783

@@ -2801,7 +2816,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
28012816

28022817
// Connect new blocks.
28032818
for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) {
2804-
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
2819+
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool, fStall)) {
28052820
if (state.IsInvalid()) {
28062821
// The block violates a consensus rule.
28072822
if (!state.CorruptionPossible()) {
@@ -2819,6 +2834,12 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar
28192834
return false;
28202835
}
28212836
} else {
2837+
if (fStall) {
2838+
// We didn't make progress because the parent chain is not
2839+
// synced enough to check pegins. Try again later.
2840+
fContinue = false;
2841+
break;
2842+
}
28222843
PruneBlockIndexCandidates();
28232844
if (!pindexOldTip || chainActive.Tip()->nChainWork > pindexOldTip->nChainWork) {
28242845
// We're in a better position than we were. Return temporarily to release the lock.
@@ -2899,6 +2920,8 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
28992920
CBlockIndex *pindexMostWork = nullptr;
29002921
CBlockIndex *pindexNewTip = nullptr;
29012922
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
2923+
bool fStall = false;
2924+
29022925
do {
29032926
boost::this_thread::interruption_point();
29042927

@@ -2930,7 +2953,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
29302953

29312954
bool fInvalidFound = false;
29322955
std::shared_ptr<const CBlock> nullBlockPtr;
2933-
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace))
2956+
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace, fStall))
29342957
return false;
29352958
blocks_connected = true;
29362959

@@ -2944,6 +2967,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
29442967
assert(trace.pblock && trace.pindex);
29452968
GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs);
29462969
}
2970+
2971+
if (fStall) {
2972+
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
2973+
break;
2974+
}
29472975
} while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip)));
29482976
if (!blocks_connected) return true;
29492977

@@ -2962,6 +2990,11 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams&
29622990
}
29632991
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
29642992

2993+
if (fStall) {
2994+
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
2995+
break;
2996+
}
2997+
29652998
if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
29662999

29673000
// We check shutdown only after giving ActivateBestChainStep a chance to run once so that we
@@ -5306,34 +5339,12 @@ class CMainCleanup
53065339
} instance_of_cmaincleanup;
53075340

53085341
// ELEMENTS:
5309-
/* This function has two major purposes:
5310-
* 1) Checks that the RPC connection to the parent chain node
5342+
/* This function checks that the RPC connection to the parent chain node
53115343
* can be attained, and is returning back reasonable answers.
5312-
* 2) Re-evaluates a list of blocks that have been deemed "bad"
5313-
* from the perspective of peg-in witness validation. Blocks are
5314-
* added to this queue in ConnectTip based on the error code returned.
53155344
*/
53165345
bool MainchainRPCCheck(const bool init)
53175346
{
5318-
// First, we can clear out any blocks thatsomehow are now deemed valid
5319-
// eg reconsiderblock rpc call manually
5320-
std::vector<uint256> vblocksToReconsider;
5321-
pblocktree->ReadInvalidBlockQueue(vblocksToReconsider);
5322-
std::vector<uint256> vblocksToReconsiderAgain;
5323-
for(uint256& blockhash : vblocksToReconsider) {
5324-
LOCK(cs_main);
5325-
if (mapBlockIndex.count(blockhash)) {
5326-
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
5327-
if ((pblockindex->nStatus & BLOCK_FAILED_MASK)) {
5328-
vblocksToReconsiderAgain.push_back(blockhash);
5329-
}
5330-
}
5331-
}
5332-
vblocksToReconsider = vblocksToReconsiderAgain;
5333-
vblocksToReconsiderAgain.clear();
5334-
pblocktree->WriteInvalidBlockQueue(vblocksToReconsider);
5335-
5336-
// Next, check for working and valid rpc
5347+
// Check for working and valid rpc
53375348
if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) {
53385349
// During init try until a non-RPC_IN_WARMUP result
53395350
while (true) {
@@ -5384,48 +5395,5 @@ bool MainchainRPCCheck(const bool init)
53845395
}
53855396
}
53865397

5387-
//Sanity startup check won't reconsider queued blocks
5388-
if (init) {
5389-
return true;
5390-
}
5391-
5392-
// Getting this far means we either aren't validating pegins(so let's make sure that's why
5393-
// it failed previously) or we successfully connected to bitcoind
5394-
// Time to reconsider blocks
5395-
if (vblocksToReconsider.size() > 0) {
5396-
CValidationState state;
5397-
for(const uint256& blockhash : vblocksToReconsider) {
5398-
{
5399-
LOCK(cs_main);
5400-
if (mapBlockIndex.count(blockhash) == 0)
5401-
continue;
5402-
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
5403-
ResetBlockFailureFlags(pblockindex);
5404-
}
5405-
}
5406-
5407-
//All blocks are now being reconsidered
5408-
ActivateBestChain(state, Params());
5409-
//This simply checks for DB errors
5410-
if (!state.IsValid()) {
5411-
//Something scary?
5412-
}
5413-
5414-
//Now to clear out now-valid blocks
5415-
for(const uint256& blockhash : vblocksToReconsider) {
5416-
LOCK(cs_main);
5417-
if (mapBlockIndex.count(blockhash)) {
5418-
CBlockIndex* pblockindex = mapBlockIndex[blockhash];
5419-
5420-
//Marked as invalid still, put back into queue
5421-
if((pblockindex->nStatus & BLOCK_FAILED_MASK)) {
5422-
vblocksToReconsiderAgain.push_back(blockhash);
5423-
}
5424-
}
5425-
}
5426-
5427-
//Write back remaining blocks
5428-
pblocktree->WriteInvalidBlockQueue(vblocksToReconsiderAgain);
5429-
}
54305398
return true;
54315399
}

0 commit comments

Comments
 (0)