Skip to content

Commit 28ba02e

Browse files
committed
Merge #1022: When validation is waiting for parent chain daemon, "stall".
e5ba4e8 Regression test for pegin validation issues during sync (Glenn Willen) 7f79f72 Finish removing 'recheckpeginblockinterval'; move MainchainRPCCheck (Glenn Willen) 4ba4e07 When validation is waiting for parent chain daemon, "stall". (Glenn Willen) Pull request description: 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. Top commit has no ACKs. Tree-SHA512: 0aa8d22460af062509f431e0f742297733d0e20b3fc1babca35f25a07c5243c7665dd0ba5fb439e3782c842a264dacd14cee6da49f8df3d1a26f5391c4742538
2 parents 5b1f3cc + e5ba4e8 commit 28ba02e

File tree

12 files changed

+164
-212
lines changed

12 files changed

+164
-212
lines changed

doc/man/elements-qt.1

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -854,12 +854,6 @@ Entries in the PAK list. Order of entries matter.
854854
.IP
855855
Pegin claims must be this deep to be considered valid. (default: 8)
856856
.HP
857-
\fB\-recheckpeginblockinterval=\fR<n>
858-
.IP
859-
The interval in seconds at which a peg\-in witness failing block is
860-
re\-evaluated in case of intermittent peg\-in witness failure. 0
861-
means never. (default: 120)
862-
.HP
863857
\fB\-validatepegin\fR
864858
.IP
865859
Validate peg\-in claims. An RPC connection will be attempted to the

doc/man/elementsd.1

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -828,12 +828,6 @@ Entries in the PAK list. Order of entries matter.
828828
.IP
829829
Pegin claims must be this deep to be considered valid. (default: 8)
830830
.HP
831-
\fB\-recheckpeginblockinterval=\fR<n>
832-
.IP
833-
The interval in seconds at which a peg\-in witness failing block is
834-
re\-evaluated in case of intermittent peg\-in witness failure. 0
835-
means never. (default: 120)
836-
.HP
837831
\fB\-validatepegin\fR
838832
.IP
839833
Validate peg\-in claims. An RPC connection will be attempted to the

src/init.cpp

Lines changed: 80 additions & 21 deletions
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);
@@ -1279,6 +1278,61 @@ bool AppInitLockDataDirectory()
12791278
return true;
12801279
}
12811280

1281+
/* This function checks that the RPC connection to the parent chain node
1282+
* can be attained, and is returning back reasonable answers.
1283+
*/
1284+
bool MainchainRPCCheck()
1285+
{
1286+
// Check for working and valid rpc
1287+
// Retry until a non-RPC_IN_WARMUP result
1288+
while (true) {
1289+
try {
1290+
// The first thing we have to check is the version of the node.
1291+
UniValue params(UniValue::VARR);
1292+
UniValue reply = CallMainChainRPC("getnetworkinfo", params);
1293+
UniValue error = reply["error"];
1294+
if (!error.isNull()) {
1295+
// On the first call, it's possible to node is still in
1296+
// warmup; in that case, just wait and retry.
1297+
if (error["code"].get_int() == RPC_IN_WARMUP) {
1298+
MilliSleep(1000);
1299+
continue;
1300+
}
1301+
else {
1302+
LogPrintf("ERROR: Mainchain daemon RPC check returned 'error' response.\n");
1303+
return false;
1304+
}
1305+
}
1306+
UniValue result = reply["result"];
1307+
if (!result.isObject() || !result.get_obj()["version"].isNum() ||
1308+
result.get_obj()["version"].get_int() < MIN_MAINCHAIN_NODE_VERSION) {
1309+
LogPrintf("ERROR: Parent chain daemon too old; need Bitcoin Core version 0.16.3 or newer.\n");
1310+
return false;
1311+
}
1312+
1313+
// Then check the genesis block to correspond to parent chain.
1314+
params.push_back(UniValue(0));
1315+
reply = CallMainChainRPC("getblockhash", params);
1316+
error = reply["error"];
1317+
if (!error.isNull()) {
1318+
LogPrintf("ERROR: Mainchain daemon RPC check returned 'error' response.\n");
1319+
return false;
1320+
}
1321+
result = reply["result"];
1322+
if (!result.isStr() || result.get_str() != Params().ParentGenesisBlockHash().GetHex()) {
1323+
LogPrintf("ERROR: Invalid parent genesis block hash response via RPC. Contacting wrong parent daemon?\n");
1324+
return false;
1325+
}
1326+
} catch (const std::runtime_error& re) {
1327+
LogPrintf("ERROR: Failure connecting to mainchain daemon RPC: %s\n", std::string(re.what()));
1328+
return false;
1329+
}
1330+
1331+
// Success
1332+
return true;
1333+
}
1334+
}
1335+
12821336
bool AppInitMain(InitInterfaces& interfaces)
12831337
{
12841338
const CChainParams& chainparams = Params();
@@ -1879,29 +1933,34 @@ bool AppInitMain(InitInterfaces& interfaces)
18791933
// ELEMENTS:
18801934
if (gArgs.GetBoolArg("-validatepegin", Params().GetConsensus().has_parent_chain)) {
18811935
uiInterface.InitMessage(_("Awaiting mainchain RPC warmup"));
1882-
}
1883-
if (!MainchainRPCCheck(true)) { //Initial check only
1884-
const std::string err_msg = "ERROR: elements is set to verify pegins but cannot get valid response from the mainchain daemon. Please check debug.log for more information.\n\nIf you haven't setup a bitcoind please get the latest stable version from https://bitcoincore.org/en/download/ or if you do not need to validate pegins set in your elements configuration validatepegin=0";
1885-
// We fail immediately if this node has RPC server enabled
1886-
if (gArgs.GetBoolArg("-server", false)) {
1887-
InitError(err_msg);
1888-
return false;
1889-
} else {
1890-
// Or gently warn the user, and continue
1891-
InitWarning(err_msg);
1892-
gArgs.SoftSetArg("-validatepegin", "0");
1936+
if (!MainchainRPCCheck()) {
1937+
const std::string err_msg = "ERROR: elements is set to verify pegins but cannot get valid response from the mainchain daemon. Please check debug.log for more information.\n\nIf you haven't setup a bitcoind please get the latest stable version from https://bitcoincore.org/en/download/ or if you do not need to validate pegins set in your elements configuration validatepegin=0";
1938+
// We fail immediately if this node has RPC server enabled
1939+
if (gArgs.GetBoolArg("-server", false)) {
1940+
InitError(err_msg);
1941+
return false;
1942+
} else {
1943+
// Or gently warn the user, and continue
1944+
InitWarning(err_msg);
1945+
gArgs.SoftSetArg("-validatepegin", "0");
1946+
}
18931947
}
18941948
}
18951949

1896-
// Start the lightweight block re-evaluation scheduler thread
1897-
CScheduler::Function reevaluationLoop = std::bind(&CScheduler::serviceQueue, &reverification_scheduler);
1898-
threadGroup.create_thread(std::bind(&TraceThread<CScheduler::Function>, "reevaluation_scheduler", reevaluationLoop));
1899-
1900-
CScheduler::Function f2 = boost::bind(&MainchainRPCCheck, false);
1901-
unsigned int check_rpc_every = gArgs.GetArg("-recheckpeginblockinterval", 120) * 1000;
1902-
if (check_rpc_every) {
1903-
reverification_scheduler.scheduleEvery(f2, check_rpc_every);
1904-
}
1950+
// Call ActivateBestChain every 30 seconds. This is almost always a
1951+
// harmless no-op. It is necessary in the unusual case where:
1952+
// (1) Our connection to bitcoind is lost, and
1953+
// (2) we build up a queue of blocks to validate in the meantime, and then
1954+
// (3) our connection to bitcoind is restored, but
1955+
// (4) nothing after that causes ActivateBestChain to be called, including
1956+
// no further blocks arriving for us to validate.
1957+
// Unfortunately, this unusual case happens in the functional test suite.
1958+
reverification_scheduler.scheduleEvery([]{
1959+
CValidationState state;
1960+
if (!ActivateBestChain(state, Params())) {
1961+
LogPrintf("Failed to periodically activate best chain (%s)\n", FormatStateMessage(state));
1962+
}
1963+
}, 30 * 1000);
19051964

19061965
uiInterface.InitMessage(_("Done loading"));
19071966

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/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
@@ -19,7 +19,7 @@ bool GetAmountFromParentChainPegin(CAmount& amount, const CTransaction& txBTC, u
1919
/** Check whether a parent chain block hash satisfies the proof-of-work requirement specified by nBits */
2020
bool CheckParentProofOfWork(uint256 hash, unsigned int nBits, const Consensus::Params&);
2121
/** Checks pegin witness for validity */
22-
bool IsValidPeginWitness(const CScriptWitness& pegin_witness, const std::vector<std::pair<CScript, CScript>>& fedpegscripts, const COutPoint& prevout, std::string& err_msg, bool check_depth);
22+
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);
2323
// Constructs unblinded output to be used in amount and scriptpubkey checks during pegin
2424
CTxOut GetPeginOutputFromWitness(const CScriptWitness& pegin_witness);
2525

src/rpc/rawtransaction.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,8 +1060,9 @@ UniValue SignTransaction(interfaces::Chain& chain, CMutableTransaction& mtx, con
10601060
continue;
10611061
}
10621062
// Report warning about immature peg-in though
1063-
if(txin.m_is_pegin && !IsValidPeginWitness(txConst.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, txin.prevout, err, true)) {
1064-
assert(err == "Needs more confirmations.");
1063+
bool depth_failed = false;
1064+
if(txin.m_is_pegin && !IsValidPeginWitness(txConst.witness.vtxinwit[i].m_pegin_witness, fedpegscripts, txin.prevout, err, true, &depth_failed)) {
1065+
assert(depth_failed);
10651066
immature_pegin = true;
10661067
}
10671068

src/txdb.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ static const char DB_LAST_BLOCK = 'l';
3434

3535
// ELEMENTS:
3636
static const char DB_PEGIN_FLAG = 'w';
37-
static const char DB_INVALID_BLOCK_Q = 'q';
37+
// static const char DB_INVALID_BLOCK_Q = 'q'; // No longer used, but avoid reuse.
3838
static const char DB_PAK = 'p';
3939

4040
namespace {
@@ -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
};

0 commit comments

Comments
 (0)