Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ BITCOIN_CORE_H = \
interfaces/node.h \
interfaces/wallet.h \
kernel/blockmanager_opts.h \
kernel/chain.h \
kernel/chainstatemanager_opts.h \
kernel/checks.h \
kernel/coinstats.h \
Expand Down Expand Up @@ -562,6 +563,7 @@ libbitcoin_node_a_SOURCES = \
instantsend/lock.cpp \
instantsend/net_instantsend.cpp \
instantsend/signing.cpp \
kernel/chain.cpp \
kernel/checks.cpp \
kernel/coinstats.cpp \
kernel/context.cpp \
Expand Down Expand Up @@ -1285,6 +1287,7 @@ libdashkernel_la_SOURCES = \
init/common.cpp \
instantsend/db.cpp \
instantsend/instantsend.cpp \
kernel/chain.cpp \
kernel/checks.cpp \
kernel/coinstats.cpp \
kernel/context.cpp \
Expand Down
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()) {
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();
}
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
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
10 changes: 7 additions & 3 deletions src/evo/creditpool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,17 @@ std::optional<CCreditPool> CCreditPoolManager::GetFromCache(const CBlockIndex& b

void CCreditPoolManager::AddToCache(const uint256& block_hash, int height, const CCreditPool &pool)
{
if (height % DISK_SNAPSHOT_PERIOD == 0) {
if (!evoDb.WriteDerived(std::make_pair(DB_CREDITPOOL_SNAPSHOT, block_hash), pool)) {
LogPrintf("ERROR: CCreditPoolManager::%s -- EvoDB credit pool mismatch for block %s\n",
__func__, block_hash.ToString());
throw std::runtime_error("EvoDB credit pool payload mismatch");
}
}
{
LOCK(cache_mutex);
creditPoolCache.insert(block_hash, pool);
}
if (height % DISK_SNAPSHOT_PERIOD == 0) {
evoDb.Write(std::make_pair(DB_CREDITPOOL_SNAPSHOT, block_hash), pool);
}
}

CCreditPool CCreditPoolManager::ConstructCreditPool(const gsl::not_null<const CBlockIndex*> block_index, CCreditPool prev)
Expand Down
Loading
Loading