Skip to content

Commit 2e01672

Browse files
knstogabrielides
authored andcommitted
fix: assertion in CMNHFManager due to missing data in evoDB (dashpay#5736)
Prior required changes: bitcoin#19438 from dashpay#5740 ## Issue being fixed or feature implemented Assertion failure: ``` assertion: ok file: evo/mnhftx.cpp, line: 287 function: AbstractEHFManager::Signals CMNHFManager::GetFromCache(const CBlockIndex*) No debug information available for stacktrace. You should add debug information and then run: dash-qt -printcrashinfo=bvcgc43iinzgc43ijfxgm3ybaadwiyltnawxc5e3ifzxgzlsoruw63ramzqws3dvojstucraebqxg43foj2gs33ohiqg62ykeaqgm2lmmu5cazlwn4xw23timz2hqltdobycyidmnfxgkoragi4docraebthk3tdoruw63r2ebawe43uojqwg5cfjbde2ylomftwk4r2hjjwsz3omfwhgicdjvheqrsnmfxgcz3foi5dur3fordhe33ninqwg2dffbrw63ttoqqegqtmn5rwwslomrsxqkrjbyrelhyaaaaaaaedgkiaaaaaaaadsm4qaaaaaaaa7gpyqaaaaaaaa2njraaaaaaaadkl3caaaaaaaabhxznqaaaaaaadqa2eaaaaaaaax33twaaaaaaabwaihqaaaaaaac7yooqbaaaaaahba45qcaaaaaacwkz2aeaaaaaaeitgeaiaaaaaaaa= dash-qt: evo/mnhftx.cpp:287: AbstractEHFManager::Signals CMNHFManager::GetFromCache(const CBlockIndex*): Assertion `ok' failed. ``` This can happen in case if Dash Core has been update from v19 (or earlier v20.alphaX) to v20.0.0 after v20 activation without re-indexing ## What was done? `CMNHFManager` is visiting missing blocks recursively until reach first v20 block or first block actually saved in evoDb. Without changes from bitcoin#19438 there's an other issue: ``` 2023-11-27T11:12:10Z POTENTIAL DEADLOCK DETECTED Previous lock order was: (2) 'cs_main' in llmq/instantsend.cpp:459 (in thread 'isman') (1) 'cs_llmq_vbc' in llmq/utils.cpp:711 (in thread 'isman') Current lock order is: 'cs_dip3list' in qt/masternodelist.cpp:135 (TRY) (in thread 'main') (1) 'cs_llmq_vbc' in llmq/utils.cpp:719 (in thread 'main') (2) 'cs_main' in node/blockstorage.cpp:77 (in thread 'main') Assertion failed: detected inconsistent lock order for 'cs_main' in node/blockstorage.cpp:77 (in thread 'main'), details in debug log. 2023-11-27T11:12:10Z Posix Signal: Aborted ``` ## How Has This Been Tested? Run unit/functional test; run dash-qt on my local backup of problematic storage (succeed without error); reindex testnet. ## Breaking Changes N/A ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_
1 parent 18b5805 commit 2e01672

File tree

2 files changed

+76
-33
lines changed

2 files changed

+76
-33
lines changed

src/evo/mnhftx.cpp

Lines changed: 60 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
#include <llmq/signing.h>
1111
#include <llmq/utils.h>
1212
#include <llmq/quorums.h>
13+
#include <node/blockstorage.h>
1314

1415
#include <chain.h>
1516
#include <chainparams.h>
1617
#include <validation.h>
1718
#include <versionbits.h>
1819

1920
#include <algorithm>
21+
#include <stack>
2022
#include <string>
2123
#include <vector>
2224

@@ -53,7 +55,7 @@ CMNHFManager::~CMNHFManager()
5355

5456
CMNHFManager::Signals CMNHFManager::GetSignalsStage(const CBlockIndex* const pindexPrev)
5557
{
56-
Signals signals = GetFromCache(pindexPrev);
58+
Signals signals = GetForBlock(pindexPrev);
5759
const int height = pindexPrev->nHeight + 1;
5860
for (auto it = signals.begin(); it != signals.end(); ) {
5961
bool found{false};
@@ -99,7 +101,7 @@ bool MNHFTx::Verify(const uint256& quorumHash, const uint256& requestId, const u
99101
return true;
100102
}
101103

102-
bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
104+
bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state)
103105
{
104106
if (tx.nVersion != 3 || tx.nType != TRANSACTION_MNHF_SIGNAL) {
105107
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-type");
@@ -114,7 +116,7 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValida
114116
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-version");
115117
}
116118

117-
const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash);
119+
const CBlockIndex* pindexQuorum = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash));
118120
if (!pindexQuorum) {
119121
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-quorum-hash");
120122
}
@@ -160,8 +162,6 @@ std::optional<uint8_t> extractEHFSignal(const CTransaction& tx)
160162

161163
static bool extractSignals(const CBlock& block, const CBlockIndex* const pindex, std::vector<uint8_t>& new_signals, BlockValidationState& state)
162164
{
163-
AssertLockHeld(cs_main);
164-
165165
// we skip the coinbase
166166
for (size_t i = 1; i < block.vtx.size(); ++i) {
167167
const CTransaction& tx = *block.vtx[i];
@@ -190,39 +190,41 @@ static bool extractSignals(const CBlock& block, const CBlockIndex* const pindex,
190190
return true;
191191
}
192192

193-
bool CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state)
193+
std::optional<CMNHFManager::Signals> CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state)
194194
{
195195
try {
196196
std::vector<uint8_t> new_signals;
197197
if (!extractSignals(block, pindex, new_signals, state)) {
198198
// state is set inside extractSignals
199-
return false;
199+
return std::nullopt;
200200
}
201201
Signals signals = GetSignalsStage(pindex->pprev);
202202
if (new_signals.empty()) {
203203
if (!fJustCheck) {
204204
AddToCache(signals, pindex);
205205
}
206206
LogPrint(BCLog::EHF, "CMNHFManager::ProcessBlock: no new signals; number of known signals: %d\n", signals.size());
207-
return true;
207+
return signals;
208208
}
209209

210-
int mined_height = pindex->nHeight;
210+
const int mined_height = pindex->nHeight;
211211

212212
// Extra validation of signals to be sure that it can succeed
213213
for (const auto& versionBit : new_signals) {
214214
LogPrintf("CMNHFManager::ProcessBlock: add mnhf bit=%d block:%s number of known signals:%lld\n", versionBit, pindex->GetBlockHash().ToString(), signals.size());
215215
if (signals.find(versionBit) != signals.end()) {
216-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-duplicate");
216+
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-duplicate");
217+
return std::nullopt;
217218
}
218219

219220
if (!Params().IsValidMNActivation(versionBit, pindex->GetMedianTimePast())) {
220-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-non-mn-fork");
221+
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-mnhf-non-mn-fork");
222+
return std::nullopt;
221223
}
222224
}
223225
if (fJustCheck) {
224226
// We are done, no need actually update any params
225-
return true;
227+
return signals;
226228
}
227229
for (const auto& versionBit : new_signals) {
228230
if (Params().IsValidMNActivation(versionBit, pindex->GetMedianTimePast())) {
@@ -232,10 +234,11 @@ bool CMNHFManager::ProcessBlock(const CBlock& block, const CBlockIndex* const pi
232234
}
233235

234236
AddToCache(signals, pindex);
235-
return true;
237+
return signals;
236238
} catch (const std::exception& e) {
237239
LogPrintf("CMNHFManager::ProcessBlock -- failed: %s\n", e.what());
238-
return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-proc-mnhf-inblock");
240+
state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-proc-mnhf-inblock");
241+
return std::nullopt;
239242
}
240243
}
241244

@@ -251,7 +254,7 @@ bool CMNHFManager::UndoBlock(const CBlock& block, const CBlockIndex* const pinde
251254
return true;
252255
}
253256

254-
const Signals signals = GetFromCache(pindex);
257+
const Signals signals = GetForBlock(pindex);
255258
for (const auto& versionBit : excluded_signals) {
256259
LogPrintf("%s: exclude mnhf bit=%d block:%s number of known signals:%lld\n", __func__, versionBit, pindex->GetBlockHash().ToString(), signals.size());
257260
assert(signals.find(versionBit) != signals.end());
@@ -261,18 +264,48 @@ bool CMNHFManager::UndoBlock(const CBlock& block, const CBlockIndex* const pinde
261264
return true;
262265
}
263266

264-
CMNHFManager::Signals CMNHFManager::GetFromCache(const CBlockIndex* const pindex)
267+
CMNHFManager::Signals CMNHFManager::GetForBlock(const CBlockIndex* pindex)
265268
{
266269
if (pindex == nullptr) return {};
267270

271+
std::stack<const CBlockIndex *> to_calculate;
272+
273+
std::optional<CMNHFManager::Signals> signalsTmp;
274+
while (!(signalsTmp = GetFromCache(pindex)).has_value()) {
275+
to_calculate.push(pindex);
276+
pindex = pindex->pprev;
277+
}
278+
279+
const Consensus::Params& consensusParams{Params().GetConsensus()};
280+
while (!to_calculate.empty()) {
281+
CBlock block;
282+
if (!ReadBlockFromDisk(block, pindex, consensusParams)) {
283+
throw std::runtime_error("failed-getehfforblock-read");
284+
}
285+
BlockValidationState state;
286+
signalsTmp = ProcessBlock(block, pindex, false, state);
287+
if (!signalsTmp.has_value()) {
288+
LogPrintf("%s: process block failed due to %s\n", __func__, state.ToString());
289+
throw std::runtime_error("failed-getehfforblock-construct");
290+
}
291+
292+
to_calculate.pop();
293+
}
294+
return *signalsTmp;
295+
}
296+
297+
std::optional<CMNHFManager::Signals> CMNHFManager::GetFromCache(const CBlockIndex* const pindex)
298+
{
299+
Signals signals{};
300+
if (pindex == nullptr) return signals;
301+
268302
// TODO: remove this check of phashBlock to nullptr
269303
// This check is needed only because unit test 'versionbits_tests.cpp'
270304
// lets `phashBlock` to be nullptr
271-
if (pindex->phashBlock == nullptr) return {};
305+
if (pindex->phashBlock == nullptr) return signals;
272306

273307

274308
const uint256& blockHash = pindex->GetBlockHash();
275-
Signals signals{};
276309
{
277310
LOCK(cs_cache);
278311
if (mnhfCache.get(blockHash, signals)) {
@@ -282,15 +315,16 @@ CMNHFManager::Signals CMNHFManager::GetFromCache(const CBlockIndex* const pindex
282315
{
283316
LOCK(cs_cache);
284317
if (ThresholdState::ACTIVE != v20_activation.State(pindex->pprev, Params().GetConsensus(), Consensus::DEPLOYMENT_V20)) {
285-
mnhfCache.insert(blockHash, {});
286-
return {};
318+
mnhfCache.insert(blockHash, signals);
319+
return signals;
287320
}
288321
}
289-
bool ok = m_evoDb.Read(std::make_pair(DB_SIGNALS, blockHash), signals);
290-
assert(ok);
291-
LOCK(cs_cache);
292-
mnhfCache.insert(blockHash, signals);
293-
return signals;
322+
if (m_evoDb.Read(std::make_pair(DB_SIGNALS, blockHash), signals)) {
323+
LOCK(cs_cache);
324+
mnhfCache.insert(blockHash, signals);
325+
return signals;
326+
}
327+
return std::nullopt;
294328
}
295329

296330
void CMNHFManager::AddToCache(const Signals& signals, const CBlockIndex* const pindex)
@@ -310,7 +344,7 @@ void CMNHFManager::AddToCache(const Signals& signals, const CBlockIndex* const p
310344

311345
void CMNHFManager::AddSignal(const CBlockIndex* const pindex, int bit)
312346
{
313-
auto signals = GetFromCache(pindex->pprev);
347+
auto signals = GetForBlock(pindex->pprev);
314348
signals.emplace(bit, pindex->nHeight);
315349
AddToCache(signals, pindex);
316350
}

src/evo/mnhftx.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ class CBlock;
2222
class CBlockIndex;
2323
class CEvoDB;
2424
class TxValidationState;
25-
extern RecursiveMutex cs_main;
2625

2726
// mnhf signal special transaction
2827
class MNHFTx
@@ -110,14 +109,17 @@ class CMNHFManager : public AbstractEHFManager
110109
explicit CMNHFManager(const CMNHFManager&) = delete;
111110

112111
/**
113-
* Every new block should be processed when Tip() is updated by calling of CMNHFManager::ProcessBlock
112+
* Every new block should be processed when Tip() is updated by calling of CMNHFManager::ProcessBlock.
113+
* This function actually does only validate EHF transaction for this block and update internal caches/evodb state
114114
*/
115-
bool ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
115+
std::optional<Signals> ProcessBlock(const CBlock& block, const CBlockIndex* const pindex, bool fJustCheck, BlockValidationState& state);
116116

117117
/**
118118
* Every undo block should be processed when Tip() is updated by calling of CMNHFManager::UndoBlock
119+
* This function actually does nothing at the moment, because status of ancester block is already know.
120+
* Altough it should be still called to do some sanity checks
119121
*/
120-
bool UndoBlock(const CBlock& block, const CBlockIndex* const pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
122+
bool UndoBlock(const CBlock& block, const CBlockIndex* const pindex);
121123

122124

123125
// Implements interface
@@ -132,13 +134,20 @@ class CMNHFManager : public AbstractEHFManager
132134

133135
/**
134136
* This function returns list of signals available on previous block.
137+
* if the signals for previous block is not available in cache it would read blocks from disk
138+
* until state won't be recovered.
135139
* NOTE: that some signals could expired between blocks.
136-
* validate them by
137140
*/
138-
Signals GetFromCache(const CBlockIndex* const pindex);
141+
Signals GetForBlock(const CBlockIndex* const pindex);
142+
143+
/**
144+
* This function access to in-memory cache or to evo db but does not calculate anything
145+
* NOTE: that some signals could expired between blocks.
146+
*/
147+
std::optional<Signals> GetFromCache(const CBlockIndex* const pindex);
139148
};
140149

141150
std::optional<uint8_t> extractEHFSignal(const CTransaction& tx);
142-
bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main);
151+
bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValidationState& state);
143152

144153
#endif // BITCOIN_EVO_MNHFTX_H

0 commit comments

Comments
 (0)