Skip to content

Commit 313f73d

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 532d55d commit 313f73d

File tree

10 files changed

+77
-117
lines changed

10 files changed

+77
-117
lines changed

src/init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,6 @@ void SetupServerArgs(NodeContext& node)
627627
argsman.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`)", ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
628628
argsman.AddArg("-mainchainrpctimeout=<n>", strprintf("Timeout in seconds during mainchain RPC requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
629629
argsman.AddArg("-peginconfirmationdepth=<n>", strprintf("Pegin claims must be this deep to be considered valid. (default: %d)", DEFAULT_PEGIN_CONFIRMATION_DEPTH), ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
630-
argsman.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), ArgsManager::ALLOW_ANY, OptionsCategory::ELEMENTS);
631630
argsman.AddArg("-parentpubkeyprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 pubkey address. (default: %d)", 111), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
632631
argsman.AddArg("-parentscriptprefix", strprintf("The byte prefix, in decimal, of the parent chain's base58 script address. (default: %d)", 196), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
633632
argsman.AddArg("-parent_bech32_hrp", strprintf("The human-readable part of the parent chain's bech32 encoding. (default: %s)", "bc"), ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);

src/mainchainrpc.cpp

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

154154
bool IsConfirmedBitcoinBlock(const uint256& hash, const int nMinConfirmationDepth, const int nbTxs)
155155
{
156+
LogPrintf("Checking for confirmed bitcoin block with hash %s, mindepth %d, nbtxs %d\n", hash.ToString().c_str(), nMinConfirmationDepth, nbTxs);
156157
try {
157158
UniValue params(UniValue::VARR);
158159
params.push_back(hash.GetHex());
159160
UniValue reply = CallMainChainRPC("getblockheader", params);
160-
if (!find_value(reply, "error").isNull())
161+
if (!find_value(reply, "error").isNull()) {
162+
LogPrintf("ERROR: Got error reply from bitcoind getblockheader.\n");
161163
return false;
164+
}
162165
UniValue result = find_value(reply, "result");
163-
if (!result.isObject())
166+
if (!result.isObject()) {
167+
LogPrintf("ERROR: bitcoind getblockheader result was malformed (not object).\n");
164168
return false;
169+
}
165170

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

src/pegins.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,11 @@ bool CheckParentProofOfWork(uint256 hash, unsigned int nBits, const Consensus::P
240240
return true;
241241
}
242242

243-
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const std::vector<std::pair<CScript, CScript>>& fedpegscripts, const COutPoint& prevout, std::string& err_msg, bool check_depth) {
243+
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const std::vector<std::pair<CScript, CScript>>& fedpegscripts, const COutPoint& prevout, std::string& err_msg, bool check_depth, bool* depth_failed) {
244+
if (depth_failed) {
245+
*depth_failed = false;
246+
}
247+
244248
// 0) Return false if !consensus.has_parent_chain
245249
if (!Params().GetConsensus().has_parent_chain) {
246250
err_msg = "Parent chain is not enabled on this network.";
@@ -372,6 +376,9 @@ bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const std::vector<
372376
LogPrintf("Required depth: %d\n", required_depth);
373377
if (!IsConfirmedBitcoinBlock(block_hash, required_depth, num_txs)) {
374378
err_msg = "Needs more confirmations.";
379+
if (depth_failed) {
380+
*depth_failed = true;
381+
}
375382
return false;
376383
}
377384
}

src/pegins.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, u
2323
/** Check whether a parent chain block hash satisfies the proof-of-work requirement specified by nBits */
2424
bool CheckParentProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
2525
/** Checks pegin witness for validity */
26-
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const std::vector<std::pair<CScript, CScript>>& fedpegscripts, const COutPoint& prevout, std::string& err_msg, bool check_depth);
26+
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const std::vector<std::pair<CScript, CScript>>& fedpegscripts, const COutPoint& prevout, std::string& err_msg, bool check_depth, bool* depth_failed = nullptr);
2727

2828
/* Consensus-critical. Matching against telescoped multisig used on Liquid v1:
2929
* Pseudo-structure:

src/rpc/rawtransaction_util.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,8 +545,9 @@ bool ValidateTransactionPeginInputs(const CMutableTransaction& mtx, std::map<int
545545
continue;
546546
}
547547
// Report warning about immature peg-in though
548-
if(txin.m_is_pegin && !IsValidPeginWitness(mtx.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, txin.prevout, err, true)) {
549-
CHECK_NONFATAL(err == "Needs more confirmations.");
548+
bool depth_failed = false;
549+
if(txin.m_is_pegin && !IsValidPeginWitness(mtx.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, txin.prevout, err, true, &depth_failed)) {
550+
CHECK_NONFATAL(depth_failed);
550551
immature_pegin = true;
551552
}
552553
}

src/txdb.cpp

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

272-
// ELEMENTS:
273-
bool CBlockTreeDB::ReadInvalidBlockQueue(std::vector<uint256> &vBlocks) {
274-
return Read(std::make_pair(DB_INVALID_BLOCK_Q, uint256S("0")), vBlocks);//FIXME: why uint 56 and not ""
275-
}
276-
bool CBlockTreeDB::WriteInvalidBlockQueue(const std::vector<uint256> &vBlocks) {
277-
return Write(std::make_pair(DB_INVALID_BLOCK_Q, uint256S("0")), vBlocks);
278-
}
279-
280272
bool CBlockTreeDB::ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject)
281273
{
282274
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
@@ -109,8 +109,6 @@ class CBlockTreeDB : public CDBWrapper
109109
bool ReadFlag(const std::string &name, bool &fValue);
110110
bool LoadBlockIndexGuts(const Consensus::Params& consensusParams, std::function<CBlockIndex*(const uint256&)> insertBlockIndex);
111111
// ELEMENTS:
112-
bool ReadInvalidBlockQueue(std::vector<uint256> &vBlocks);
113-
bool WriteInvalidBlockQueue(const std::vector<uint256> &vBlocks);
114112
bool ReadPAKList(std::vector<std::vector<unsigned char> >& offline_list, std::vector<std::vector<unsigned char> >& online_list, bool& reject);
115113
bool WritePAKList(const std::vector<std::vector<unsigned char> >& offline_list, const std::vector<std::vector<unsigned char> >& online_list, bool reject);
116114
};

src/validation.cpp

Lines changed: 55 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,6 +2083,29 @@ static int64_t nTimeCallbacks = 0;
20832083
static int64_t nTimeTotal = 0;
20842084
static int64_t nBlocksTotal = 0;
20852085

2086+
bool CheckPeginRipeness(const CBlock& block, const std::vector<std::pair<CScript, CScript>>& fedpegscripts) {
2087+
for (unsigned int i = 0; i < block.vtx.size(); i++) {
2088+
const CTransaction &tx = *(block.vtx[i]);
2089+
2090+
if (!tx.IsCoinBase()) {
2091+
for (unsigned int i = 0; i < tx.vin.size(); ++i) {
2092+
if (tx.vin[i].m_is_pegin) {
2093+
std::string err;
2094+
bool depth_failed = false;
2095+
if ((tx.witness.vtxinwit.size() <= i) || !IsValidPeginWitness(tx.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, tx.vin[i].prevout, err, true, &depth_failed)) {
2096+
if (depth_failed) {
2097+
return false; // Pegins not ripe.
2098+
} else {
2099+
return true; // Some other failure; details later.
2100+
}
2101+
}
2102+
}
2103+
}
2104+
}
2105+
}
2106+
return true;
2107+
}
2108+
20862109
/** Apply the effects of this block (with given index) on the UTXO set represented by coins.
20872110
* Validity checks that depend on the UTXO set are also done; ConnectBlock()
20882111
* can fail if those validity checks fail (among other reasons). */
@@ -2820,7 +2843,7 @@ class ConnectTrace {
28202843
*
28212844
* The block is added to connectTrace if connection succeeds.
28222845
*/
2823-
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool)
2846+
bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexNew, const std::shared_ptr<const CBlock>& pblock, ConnectTrace& connectTrace, DisconnectedBlockTransactions &disconnectpool, bool& fStall)
28242847
{
28252848
AssertLockHeld(cs_main);
28262849
AssertLockHeld(m_mempool.cs);
@@ -2838,6 +2861,14 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
28382861
pthisBlock = pblock;
28392862
}
28402863
const CBlock& blockConnecting = *pthisBlock;
2864+
2865+
const auto& fedpegscripts = GetValidFedpegScripts(pindexNew, chainparams.GetConsensus(), false /* nextblock_validation */);
2866+
if (!CheckPeginRipeness(blockConnecting, fedpegscripts)) {
2867+
LogPrintf("STALLING further progress in ConnectTip while waiting for parent chain daemon to catch up! Chain will not grow until this is remedied!\n");
2868+
fStall = true;
2869+
return true;
2870+
}
2871+
28412872
// Apply the block atomically to the chain state.
28422873
int64_t nTime2 = GetTimeMicros(); nTimeReadFromDisk += nTime2 - nTime1;
28432874
int64_t nTime3;
@@ -2854,29 +2885,6 @@ bool CChainState::ConnectTip(BlockValidationState& state, const CChainParams& ch
28542885
if (!rv) {
28552886
if (state.IsInvalid()) {
28562887
InvalidBlockFound(pindexNew, state);
2857-
2858-
// ELEMENTS:
2859-
// Possibly result of RPC to mainchain bitcoind failure
2860-
// or unseen Bitcoin blocks.
2861-
// These blocks are later re-evaluated at an interval
2862-
// set by `-recheckpeginblockinterval`.
2863-
if (state.GetRejectReason() == "bad-pegin-witness") {
2864-
//Write queue of invalid blocks that
2865-
//must be cleared to continue operation
2866-
std::vector<uint256> vinvalidBlocks;
2867-
pblocktree->ReadInvalidBlockQueue(vinvalidBlocks);
2868-
bool blockAlreadyInvalid = false;
2869-
for (uint256& hash : vinvalidBlocks) {
2870-
if (hash == blockConnecting.GetHash()) {
2871-
blockAlreadyInvalid = true;
2872-
break;
2873-
}
2874-
}
2875-
if (!blockAlreadyInvalid) {
2876-
vinvalidBlocks.push_back(blockConnecting.GetHash());
2877-
pblocktree->WriteInvalidBlockQueue(vinvalidBlocks);
2878-
}
2879-
}
28802888
}
28812889
return error("%s: ConnectBlock %s failed, %s", __func__, pindexNew->GetBlockHash().ToString(), state.ToString());
28822890
}
@@ -2988,7 +2996,7 @@ void CChainState::PruneBlockIndexCandidates() {
29882996
*
29892997
* @returns true unless a system error occurred
29902998
*/
2991-
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace)
2999+
bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace, bool& fStall)
29923000
{
29933001
AssertLockHeld(cs_main);
29943002
AssertLockHeld(m_mempool.cs);
@@ -3033,7 +3041,7 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
30333041

30343042
// Connect new blocks.
30353043
for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) {
3036-
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) {
3044+
if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool, fStall)) {
30373045
if (state.IsInvalid()) {
30383046
// The block violates a consensus rule.
30393047
if (state.GetResult() != BlockValidationResult::BLOCK_MUTATED) {
@@ -3051,6 +3059,12 @@ bool CChainState::ActivateBestChainStep(BlockValidationState& state, const CChai
30513059
return false;
30523060
}
30533061
} else {
3062+
if (fStall) {
3063+
// We didn't make progress because the parent chain is not
3064+
// synced enough to check pegins. Try again later.
3065+
fContinue = false;
3066+
break;
3067+
}
30543068
PruneBlockIndexCandidates();
30553069
if (!pindexOldTip || m_chain.Tip()->nChainWork > pindexOldTip->nChainWork) {
30563070
// 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
31303144
CBlockIndex *pindexMostWork = nullptr;
31313145
CBlockIndex *pindexNewTip = nullptr;
31323146
int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT);
3147+
bool fStall = false;
3148+
31333149
do {
31343150
// Block until the validation queue drains. This should largely
31353151
// never happen in normal operation, however may happen during
@@ -3160,7 +3176,7 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
31603176

31613177
bool fInvalidFound = false;
31623178
std::shared_ptr<const CBlock> nullBlockPtr;
3163-
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) {
3179+
if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace, fStall)) {
31643180
// A system error occurred
31653181
return false;
31663182
}
@@ -3176,6 +3192,11 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
31763192
assert(trace.pblock && trace.pindex);
31773193
GetMainSignals().BlockConnected(trace.pblock, trace.pindex);
31783194
}
3195+
3196+
if (fStall) {
3197+
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
3198+
break;
3199+
}
31793200
} while (!m_chain.Tip() || (starting_tip && CBlockIndexWorkComparator()(m_chain.Tip(), starting_tip)));
31803201
if (!blocks_connected) return true;
31813202

@@ -3194,6 +3215,11 @@ bool CChainState::ActivateBestChain(BlockValidationState &state, const CChainPar
31943215
}
31953216
// When we reach this point, we switched to a new tip (stored in pindexNewTip).
31963217

3218+
if (fStall) {
3219+
// Stuck waiting for parent chain daemon, twiddle our thumbs for awhile.
3220+
break;
3221+
}
3222+
31973223
if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown();
31983224

31993225
// We check shutdown only after giving ActivateBestChainStep a chance to run once so that we
@@ -5711,35 +5737,12 @@ void ChainstateManager::MaybeRebalanceCaches()
57115737
}
57125738

57135739
// ELEMENTS:
5714-
/* This function has two major purposes:
5715-
* 1) Checks that the RPC connection to the parent chain node
5740+
/* This function checks that the RPC connection to the parent chain node
57165741
* can be attained, and is returning back reasonable answers.
5717-
* 2) Re-evaluates a list of blocks that have been deemed "bad"
5718-
* from the perspective of peg-in witness validation. Blocks are
5719-
* added to this queue in ConnectTip based on the error code returned.
57205742
*/
57215743
bool MainchainRPCCheck(const bool init)
57225744
{
5723-
// First, we can clear out any blocks thatsomehow are now deemed valid
5724-
// eg reconsiderblock rpc call manually
5725-
std::vector<uint256> vblocksToReconsider;
5726-
pblocktree->ReadInvalidBlockQueue(vblocksToReconsider);
5727-
std::vector<uint256> vblocksToReconsiderAgain;
5728-
for(uint256& blockhash : vblocksToReconsider) {
5729-
LOCK(cs_main);
5730-
ChainstateManager& chainman = g_chainman;
5731-
if (chainman.BlockIndex().count(blockhash)) {
5732-
CBlockIndex* pblockindex = chainman.BlockIndex()[blockhash];
5733-
if ((pblockindex->nStatus & BLOCK_FAILED_MASK)) {
5734-
vblocksToReconsiderAgain.push_back(blockhash);
5735-
}
5736-
}
5737-
}
5738-
vblocksToReconsider = vblocksToReconsiderAgain;
5739-
vblocksToReconsiderAgain.clear();
5740-
pblocktree->WriteInvalidBlockQueue(vblocksToReconsider);
5741-
5742-
// Next, check for working and valid rpc
5745+
// Check for working and valid rpc
57435746
if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) {
57445747
// During init try until a non-RPC_IN_WARMUP result
57455748
while (true) {
@@ -5790,50 +5793,5 @@ bool MainchainRPCCheck(const bool init)
57905793
}
57915794
}
57925795

5793-
//Sanity startup check won't reconsider queued blocks
5794-
if (init) {
5795-
return true;
5796-
}
5797-
5798-
// Getting this far means we either aren't validating pegins(so let's make sure that's why
5799-
// it failed previously) or we successfully connected to bitcoind
5800-
// Time to reconsider blocks
5801-
if (vblocksToReconsider.size() > 0) {
5802-
BlockValidationState state;
5803-
for(const uint256& blockhash : vblocksToReconsider) {
5804-
{
5805-
LOCK(cs_main);
5806-
ChainstateManager& chainman = g_chainman;
5807-
if (chainman.BlockIndex().count(blockhash) == 0)
5808-
continue;
5809-
CBlockIndex* pblockindex = chainman.BlockIndex()[blockhash];
5810-
ResetBlockFailureFlags(pblockindex);
5811-
}
5812-
}
5813-
5814-
//All blocks are now being reconsidered
5815-
ActivateBestChain(state, Params());
5816-
//This simply checks for DB errors
5817-
if (!state.IsValid()) {
5818-
//Something scary?
5819-
}
5820-
5821-
//Now to clear out now-valid blocks
5822-
for(const uint256& blockhash : vblocksToReconsider) {
5823-
LOCK(cs_main);
5824-
ChainstateManager& chainman = g_chainman;
5825-
if (chainman.BlockIndex().count(blockhash)) {
5826-
CBlockIndex* pblockindex = chainman.BlockIndex()[blockhash];
5827-
5828-
//Marked as invalid still, put back into queue
5829-
if((pblockindex->nStatus & BLOCK_FAILED_MASK)) {
5830-
vblocksToReconsiderAgain.push_back(blockhash);
5831-
}
5832-
}
5833-
}
5834-
5835-
//Write back remaining blocks
5836-
pblocktree->WriteInvalidBlockQueue(vblocksToReconsiderAgain);
5837-
}
58385796
return true;
58395797
}

0 commit comments

Comments
 (0)