Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
990d950
evo: make CEvoDB consistency state per-chainstate
PastaPastaPasta Jul 11, 2026
3775ae4
evo: wire per-chainstate transactions into validation
PastaPastaPasta Jul 11, 2026
8cd55e6
test: add dual-chainstate EvoDB consistency coverage
PastaPastaPasta Jul 11, 2026
2fc3c51
dash: bind block validation to the calling chainstate
PastaPastaPasta Jul 11, 2026
f5c93ce
validation: suppress background chainstate notifications
PastaPastaPasta Jul 11, 2026
d3aea54
dash: guard serving unavailable snapshot history
PastaPastaPasta Jul 11, 2026
8010353
dash: refuse masternode duty on unvalidated snapshots
PastaPastaPasta Jul 11, 2026
341972d
fix: canonicalize serialization of block-derived EvoDB payloads
PastaPastaPasta Jul 11, 2026
2cb2a02
test: adapt AssumeUTXO fixtures after txindex removal
PastaPastaPasta Jul 14, 2026
ec7ec1a
evo: abort node on WriteDerived mismatch instead of consensus rejection
PastaPastaPasta Aug 1, 2026
7ff8231
evo: preserve EvoDB inconsistency classification across GetForBlock r…
PastaPastaPasta Aug 1, 2026
0e6172d
evo: resolve transaction-less EvoDB access against the active chainstate
PastaPastaPasta Aug 1, 2026
a1fbefc
dash: tolerate unavailable masternode list in scheduler-thread callbacks
PastaPastaPasta Aug 1, 2026
5639e45
dash: only swallow unavailable-history errors from GetListForBlock
PastaPastaPasta Aug 4, 2026
311efbc
validation: establish the connecting chainstate's BLS scheme
PastaPastaPasta Aug 4, 2026
6f2aabc
evo: scope an open EvoDB transaction to the thread that began it
PastaPastaPasta Aug 4, 2026
066fca8
validation: discard snapshot state when the snapshot chainstate goes …
PastaPastaPasta Aug 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/Makefile.test.include
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ BITCOIN_TESTS =\
test/dynamic_activation_thresholds_tests.cpp \
test/evo_assetlocks_tests.cpp \
test/evo_cbtx_tests.cpp \
test/evo_db_tests.cpp \
test/evo_deterministicmns_tests.cpp \
test/evo_islock_tests.cpp \
test/evo_mnhf_tests.cpp \
Expand Down
13 changes: 13 additions & 0 deletions src/active/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <llmq/quorums.h>
#include <llmq/quorumsman.h>
#include <llmq/signing_shares.h>
#include <logging.h>
#include <masternode/sync.h>
#include <util/check.h>
#include <validation.h>
Expand All @@ -35,6 +36,7 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman
const CBLSSecretKey& operator_sk, const util::DbWrapperParams& db_params, bool quorums_watch) :
llmq::QuorumRole{qman},
m_bls_worker{bls_worker},
m_chainman{chainman},
m_quorums_watch{quorums_watch},
nodeman{std::make_unique<CActiveMasternodeManager>(connman, dmnman, operator_sk)},
dkgdbgman{std::make_unique<llmq::CDKGDebugManager>(dmnman, qsnapman, chainman)},
Expand Down Expand Up @@ -94,6 +96,17 @@ void ActiveContext::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIn
return;

nodeman->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);

if (m_chainman.IsSnapshotActiveAndUnvalidated()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move snapshot gate before masternode tip update

This guard runs after nodeman->UpdatedBlockTip(), so an active masternode that is not yet READY during an unvalidated snapshot can enter CActiveMasternodeManager::UpdatedBlockTip()'s Init(pindexNew) path before the gate is checked; InitInternal() calls GetListForBlock(pindexNew) without the new BlockDataUnavailableError catch, and that uncaught exception is on the scheduler validation callback path. In the same missing-history snapshot scenario this gate is meant to suppress, the node can terminate before DKG/signing is disabled, so check IsSnapshotActiveAndUnvalidated() before calling nodeman or catch the unavailable-list exception in the init path as well.

AGENTS.md reference: AGENTS.md:L168-L170

Useful? React with 👍 / 👎.

if (!m_snapshot_duty_blocked.exchange(true)) {
LogPrintf("Masternode DKG participation and quorum signing are disabled until snapshot background validation completes\n");
}
return;
}
if (m_snapshot_duty_blocked.exchange(false)) {
LogPrintf("Snapshot background validation completed; masternode DKG participation and quorum signing are enabled\n");
}

ehf_sighandler->UpdatedBlockTip(pindexNew);
gov_signer->UpdatedBlockTip(pindexNew);
qdkgsman->UpdatedBlockTip(pindexNew, fInitialDownload);
Expand Down
3 changes: 3 additions & 0 deletions src/active/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <gsl/pointers.h>
#include <span.h>

#include <atomic>
#include <memory>

class CActiveMasternodeManager;
Expand Down Expand Up @@ -49,7 +50,9 @@ struct DbWrapperParams;
struct ActiveContext final : public llmq::QuorumRole, public CValidationInterface {
private:
CBLSWorker& m_bls_worker;
ChainstateManager& m_chainman;
const bool m_quorums_watch{false};
std::atomic_bool m_snapshot_duty_blocked{false};

public:
ActiveContext() = delete;
Expand Down
23 changes: 23 additions & 0 deletions src/active/dkgsessionhandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <deploymentstatus.h>
#include <logging.h>
#include <util/time.h>
#include <validation.h>

namespace llmq {
ActiveDKGSessionHandler::ActiveDKGSessionHandler(
Expand Down Expand Up @@ -41,6 +42,8 @@ ActiveDKGSessionHandler::~ActiveDKGSessionHandler() = default;

void ActiveDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew)
{
if (m_chainman.IsSnapshotActiveAndUnvalidated()) return;

//AssertLockNotHeld(cs_main);
//Indexed quorums (greater than 0) are enabled with Quorum Rotation
if (quorumIndex > 0 && !IsQuorumRotationEnabled(params, pindexNew)) {
Expand Down Expand Up @@ -76,6 +79,10 @@ std::pair<QuorumPhase, uint256> ActiveDKGSessionHandler::GetPhaseAndQuorumHash()

bool ActiveDKGSessionHandler::InitNewQuorum(gsl::not_null<const CBlockIndex*> pQuorumBaseBlockIndex)
{
if (m_chainman.IsSnapshotActiveAndUnvalidated()) {
LogPrint(BCLog::LLMQ_DKG, "%s -- refusing DKG participation while snapshot background validation is incomplete\n", __func__);
return false;
}
if (!DeploymentDIP0003Enforced(pQuorumBaseBlockIndex->nHeight, Params().GetConsensus())) {
return false;
}
Expand All @@ -100,6 +107,10 @@ void ActiveDKGSessionHandler::WaitForNextPhase(std::optional<QuorumPhase> curPha
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? std23::to_underlying(*curPhase) : -1, std23::to_underlying(nextPhase));

while (true) {
if (m_chainman.IsSnapshotActiveAndUnvalidated()) {
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting because snapshot background validation is incomplete\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (stopRequested) {
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
Expand Down Expand Up @@ -139,6 +150,10 @@ void ActiveDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) con
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d]- starting\n", __func__, params.name, quorumIndex);

while (true) {
if (m_chainman.IsSnapshotActiveAndUnvalidated()) {
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting because snapshot background validation is incomplete\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
}
if (stopRequested) {
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
Expand Down Expand Up @@ -186,6 +201,10 @@ void ActiveDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, const uint2
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, std23::to_underlying(curPhase));

while (SteadyClock::now() < endTime) {
if (m_chainman.IsSnapshotActiveAndUnvalidated()) {
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting because snapshot background validation is incomplete\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
}
if (stopRequested) {
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex);
throw AbortPhaseException();
Expand Down Expand Up @@ -220,6 +239,10 @@ void ActiveDKGSessionHandler::HandlePhase(QuorumPhase curPhase, QuorumPhase next
LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, std23::to_underlying(curPhase), std23::to_underlying(nextPhase));

SleepBeforePhase(curPhase, expectedQuorumHash, randomSleepFactor, runWhileWaiting);
if (m_chainman.IsSnapshotActiveAndUnvalidated()) {
LogPrint(BCLog::LLMQ_DKG, "%s -- refusing DKG participation while snapshot background validation is incomplete\n", __func__);
throw AbortPhaseException();
}
startPhaseFunc();
WaitForNextPhase(curPhase, nextPhase, expectedQuorumHash, runWhileWaiting);

Expand Down
17 changes: 15 additions & 2 deletions src/active/masternode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,21 @@ void CActiveMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, con

const auto [cur_state, cur_protx_hash] = WITH_READ_LOCK(cs, return std::make_pair(m_state, m_protx_hash));
if (cur_state == MasternodeState::READY) {
auto oldMNList = m_dmnman.GetListForBlock(pindexNew->pprev);
auto newMNList = m_dmnman.GetListForBlock(pindexNew);
CDeterministicMNList oldMNList;
CDeterministicMNList newMNList;
try {
oldMNList = m_dmnman.GetListForBlock(pindexNew->pprev);
newMNList = m_dmnman.GetListForBlock(pindexNew);
} catch (const BlockDataUnavailableError& e) {
// This callback runs on the scheduler thread, where an uncaught
// exception terminates the node. Unavailable history is expected
// while a snapshot's background chainstate is still catching up, so
// skip this tip update and let the next one retry. Any other
// exception means local EvoDB/list corruption and must not be
// hidden, so it deliberately stays unhandled.
LogPrintf("CActiveMasternodeManager::%s -- masternode list unavailable: %s\n", __func__, e.what());
return;
Comment thread
PastaPastaPasta marked this conversation as resolved.
}
auto reset = [this, pindexNew](MasternodeState state) -> void {
LOCK(cs);
m_state = state;
Expand Down
16 changes: 16 additions & 0 deletions src/dbwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,22 @@ class CDBTransaction {
return parent.Read(ssKey, value);
}

/** Read a value only if it is present in this transaction's write set. */
template <typename K, typename V>
bool ReadPending(const K& key, V& value) {
const CDataStream ssKey = KeyToDataStream(key);
auto it = writes.find(ssKey);
if (it == writes.end()) {
return false;
}
auto* impl = dynamic_cast<ValueHolderImpl<V>*>(it->second.get());
if (!impl) {
throw std::runtime_error("ReadPending called with V != previously written type");
}
value = impl->value;
return true;
}

template <typename K>
bool Exists(const K& key) {
return Exists(KeyToDataStream(key));
Expand Down
70 changes: 59 additions & 11 deletions src/evo/assetlocktx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ std::string CAssetLockPayload::ToString() const

const std::string ASSETUNLOCK_REQUESTID_PREFIX = "plwdtx";

bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const uint256& msgHash, gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state) const
template <typename ScanQuorums, typename GetQuorum>
static bool VerifyAssetUnlockSig(const CAssetUnlockPayload& payload, ScanQuorums&& scan_quorums,
GetQuorum&& get_quorum, const uint256& msgHash,
gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state)
{
// That quourm hash must be active at `requestHeight`,
// and at the quorumHash must be active in either the current or previous quorum cycle
Expand All @@ -110,36 +113,60 @@ bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const uint

// We check all active quorums + 1 the latest inactive
const int quorums_to_scan = llmq_params_opt->signingActiveQuorumCount + 1;
const auto quorums = qman.ScanQuorums(llmqType, pindexTip, quorums_to_scan);
const auto quorums = scan_quorums(llmqType, pindexTip, quorums_to_scan);

if (bool isActive = std::any_of(quorums.begin(), quorums.end(), [&](const auto &q) { return q->qc->quorumHash == quorumHash; }); !isActive) {
if (bool isActive = std::any_of(quorums.begin(), quorums.end(), [&](const auto &q) { return q->qc->quorumHash == payload.getQuorumHash(); }); !isActive) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-too-old-quorum");
}

if (static_cast<uint32_t>(pindexTip->nHeight) < requestedHeight || pindexTip->nHeight >= getHeightToExpiry()) {
if (static_cast<uint32_t>(pindexTip->nHeight) < payload.getRequestedHeight() || pindexTip->nHeight >= payload.getHeightToExpiry()) {
LogPrint(BCLog::CREDITPOOL, "Asset unlock tx %d with requested height %d could not be accepted on height: %d\n",
index, requestedHeight, pindexTip->nHeight);
payload.getIndex(), payload.getRequestedHeight(), pindexTip->nHeight);
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-too-late");
}

const auto quorum = qman.GetQuorum(llmqType, quorumHash);
const auto quorum = get_quorum(llmqType, payload.getQuorumHash());
// quorum must be valid at this point. Let's check and throw error just in case
if (!quorum) {
LogPrintf("%s: ERROR! No quorum for credit pool found for hash=%s\n", __func__, quorumHash.ToString());
LogPrintf("%s: ERROR! No quorum for credit pool found for hash=%s\n", __func__, payload.getQuorumHash().ToString());
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-quorum-internal-error");
}

const uint256 requestId = ::SerializeHash(std::make_pair(ASSETUNLOCK_REQUESTID_PREFIX, index));
const uint256 requestId = ::SerializeHash(std::make_pair(ASSETUNLOCK_REQUESTID_PREFIX, payload.getIndex()));

if (const llmq::SignHash signHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
quorumSig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get())) {
payload.getQuorumSig().VerifyInsecure(quorum->qc->quorumPublicKey, signHash.Get())) {
return true;
}

return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-assetunlock-not-verified");
}

bool CheckAssetUnlockTx(const BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, const std::optional<CRangesSet>& indexes, TxValidationState& state)
bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const uint256& msgHash,
gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state) const
{
return VerifyAssetUnlockSig(*this, [&](Consensus::LLMQType llmq_type, const CBlockIndex* pindex, size_t count) {
return qman.ScanQuorums(llmq_type, pindex, count);
}, [&](Consensus::LLMQType llmq_type, const uint256& quorum_hash) {
return qman.GetQuorum(llmq_type, quorum_hash);
}, msgHash, pindexTip, state);
}

bool CAssetUnlockPayload::VerifySig(const llmq::CQuorumManager& qman, const CChain& chain, const uint256& msgHash,
gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state) const
{
AssertLockHeld(::cs_main);
return VerifyAssetUnlockSig(*this, [&](Consensus::LLMQType llmq_type, const CBlockIndex* pindex, size_t count) NO_THREAD_SAFETY_ANALYSIS {
return qman.ScanQuorums(llmq_type, pindex, count, chain);
}, [&](Consensus::LLMQType llmq_type, const uint256& quorum_hash) NO_THREAD_SAFETY_ANALYSIS {
return qman.GetQuorum(llmq_type, quorum_hash, chain);
}, msgHash, pindexTip, state);
}

template <typename VerifySig>
static bool CheckAssetUnlockTxImpl(const BlockManager& blockman, VerifySig&& verify_sig, const CTransaction& tx,
gsl::not_null<const CBlockIndex*> pindexPrev,
const std::optional<CRangesSet>& indexes, TxValidationState& state)
{
// Some checks depends from blockchain status also, such as `known indexes` and `withdrawal limits`
// They are omitted here and done by CCreditPool
Expand Down Expand Up @@ -180,7 +207,28 @@ bool CheckAssetUnlockTx(const BlockManager& blockman, const llmq::CQuorumManager

uint256 msgHash = tx_copy.GetHash();

return assetUnlockTx.VerifySig(qman, msgHash, pindexPrev, state);
return verify_sig(assetUnlockTx, msgHash, pindexPrev, state);
}

bool CheckAssetUnlockTx(const BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx,
gsl::not_null<const CBlockIndex*> pindexPrev, const std::optional<CRangesSet>& indexes,
TxValidationState& state)
{
return CheckAssetUnlockTxImpl(blockman, [&](const CAssetUnlockPayload& payload, const uint256& msg_hash,
const CBlockIndex* pindex, TxValidationState& tx_state) {
return payload.VerifySig(qman, msg_hash, pindex, tx_state);
}, tx, pindexPrev, indexes, state);
}

bool CheckAssetUnlockTx(const BlockManager& blockman, const llmq::CQuorumManager& qman, const CChain& chain,
const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
const std::optional<CRangesSet>& indexes, TxValidationState& state)
{
AssertLockHeld(::cs_main);
return CheckAssetUnlockTxImpl(blockman, [&](const CAssetUnlockPayload& payload, const uint256& msg_hash,
const CBlockIndex* pindex, TxValidationState& tx_state) NO_THREAD_SAFETY_ANALYSIS {
return payload.VerifySig(qman, chain, msg_hash, pindex, tx_state);
}, tx, pindexPrev, indexes, state);
}

bool GetAssetUnlockFee(const CTransaction& tx, CAmount& txfee, TxValidationState& state)
Expand Down
11 changes: 11 additions & 0 deletions src/evo/assetlocktx.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@
#include <gsl/pointers.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <sync.h>
#include <threadsafety.h>
#include <univalue.h>

#include <optional>

class CBlockIndex;
class CChain;
class CRangesSet;
class TxValidationState;
extern RecursiveMutex cs_main; // NOLINT(readability-redundant-declaration)
struct RPCResult;
namespace llmq {
class CQuorumManager;
Expand Down Expand Up @@ -114,6 +118,9 @@ class CAssetUnlockPayload
[[nodiscard]] UniValue ToJson() const;

bool VerifySig(const llmq::CQuorumManager& qman, const uint256& msgHash, gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state) const;
bool VerifySig(const llmq::CQuorumManager& qman, const CChain& chain, const uint256& msgHash,
gsl::not_null<const CBlockIndex*> pindexTip, TxValidationState& state) const
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);

// getters
uint8_t getVersion() const
Expand Down Expand Up @@ -156,6 +163,10 @@ class CAssetUnlockPayload

bool CheckAssetLockTx(const CTransaction& tx, TxValidationState& state);
bool CheckAssetUnlockTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev, const std::optional<CRangesSet>& indexes, TxValidationState& state);
bool CheckAssetUnlockTx(const node::BlockManager& blockman, const llmq::CQuorumManager& qman, const CChain& chain,
const CTransaction& tx, gsl::not_null<const CBlockIndex*> pindexPrev,
const std::optional<CRangesSet>& indexes, TxValidationState& state)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
bool GetAssetUnlockFee(const CTransaction& tx, CAmount& txfee, TxValidationState& state);

#endif // BITCOIN_EVO_ASSETLOCKTX_H
2 changes: 1 addition & 1 deletion src/evo/chainhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ bool CChainstateHelper::RemoveConflictingISLockByTx(const CTransaction& tx)
return true;
}

std::unordered_map<uint8_t, int> CChainstateHelper::GetSignalsStage(const CBlockIndex* const pindexPrev)
std::map<uint8_t, int> CChainstateHelper::GetSignalsStage(const CBlockIndex* const pindexPrev)
{
return ehf_manager->GetSignalsStage(pindexPrev);
}
4 changes: 2 additions & 2 deletions src/evo/chainhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
#define BITCOIN_EVO_CHAINHELPER_H

#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <unordered_map>

class CBlockIndex;
class CCreditPoolManager;
Expand Down Expand Up @@ -77,7 +77,7 @@ class CChainstateHelper
bool IsInstantSendWaitingForTx(const uint256& hash) const;
bool RemoveConflictingISLockByTx(const CTransaction& tx);

std::unordered_map<uint8_t, int> GetSignalsStage(const CBlockIndex* const pindexPrev);
std::map<uint8_t, int> GetSignalsStage(const CBlockIndex* const pindexPrev);
};

#endif // BITCOIN_EVO_CHAINHELPER_H
Loading
Loading