Skip to content
Merged
14 changes: 8 additions & 6 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -213,22 +213,24 @@ BITCOIN_CORE_H = \
key_io.h \
dbwrapper.h \
limitedmap.h \
llmq/quorums.h \
llmq/blockprocessor.h \
llmq/commitment.h \
llmq/chainlocks.h \
llmq/clsig.h \
llmq/commitment.h \
llmq/context.h \
llmq/debug.h \
llmq/dkgsession.h \
llmq/dkgsessionhandler.h \
llmq/dkgsessionmgr.h \
llmq/dkgsession.h \
llmq/context.h \
llmq/ehf_signals.cpp \
llmq/ehf_signals.h \
llmq/instantsend.h \
llmq/snapshot.h \
llmq/params.h \
llmq/quorums.h \
llmq/signing.h \
llmq/signing_shares.h \
llmq/snapshot.h \
llmq/utils.h \
llmq/params.h \
logging.h \
logging/timer.h \
mapport.h \
Expand Down
12 changes: 8 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,11 @@ class CMainParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 19999999999; // TODO: To be determined later
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 4032; // TODO to be determined before v20 release: choose nWindowSize/nThresholdStart/nThresholdMin
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 2420; // 60% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nMNActivationHeight = 0; // requires EHF activation

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000008677827656704520eb39"); // 1889000
Expand Down Expand Up @@ -420,6 +421,7 @@ class CTestNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nMNActivationHeight = 0; // requires EHF activation

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68c8cc1b8e54b"); // 851000
Expand Down Expand Up @@ -591,6 +593,7 @@ class CDevNetParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nMNActivationHeight = 0; // requires EHF activation

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000");
Expand Down Expand Up @@ -826,10 +829,11 @@ class CRegTestParams : public CChainParams {
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].bit = 10;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 1030;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 800; // 80% of 1000
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 600; // 60% of 1000
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nWindowSize = 12;
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdStart = 9; // 80% of 12
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nThresholdMin = 7; // 60% of 7
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nFalloffCoeff = 5; // this corresponds to 10 periods
consensus.vDeployments[Consensus::DEPLOYMENT_MN_RR].nMNActivationHeight = 0; // requires EHF activation

// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
Expand Down
2 changes: 2 additions & 0 deletions src/dsnotificationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <llmq/chainlocks.h>
#include <llmq/context.h>
#include <llmq/dkgsessionmgr.h>
#include <llmq/ehf_signals.h>
#include <llmq/instantsend.h>
#include <llmq/quorums.h>

Expand Down Expand Up @@ -78,6 +79,7 @@ void CDSNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindexNew, con

llmq_ctx->qman->UpdatedBlockTip(pindexNew, fInitialDownload);
llmq_ctx->qdkgsman->UpdatedBlockTip(pindexNew, fInitialDownload);
llmq_ctx->ehfSignalsHandler->UpdatedBlockTip(pindexNew);

if (!fDisableGovernance) govman.UpdatedBlockTip(pindexNew, connman);
}
Expand Down
22 changes: 18 additions & 4 deletions src/evo/mnhftx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,24 @@
#include <string>
#include <vector>

extern const std::string MNEHF_REQUESTID_PREFIX = "mnhf";
static const std::string MNEHF_REQUESTID_PREFIX = "mnhf";
static const std::string DB_SIGNALS = "mnhf_s";

uint256 MNHFTxPayload::GetRequestId() const
{
return ::SerializeHash(std::make_pair(MNEHF_REQUESTID_PREFIX, int64_t{signal.versionBit}));
}

CMutableTransaction MNHFTxPayload::PrepareTx() const
{
CMutableTransaction tx;
tx.nVersion = 3;
tx.nType = SPECIALTX_TYPE;
SetTxPayload(tx, *this);

return tx;
}

CMNHFManager::Signals CMNHFManager::GetSignalsStage(const CBlockIndex* const pindexPrev)
{
Signals signals = GetFromCache(pindexPrev);
Expand Down Expand Up @@ -53,7 +68,7 @@ CMNHFManager::Signals CMNHFManager::GetSignalsStage(const CBlockIndex* const pin
return signals;
}

bool MNHFTx::Verify(const uint256& quorumHash, const uint256& msgHash, TxValidationState& state) const
bool MNHFTx::Verify(const uint256& quorumHash, const uint256& requestId, const uint256& msgHash, TxValidationState& state) const
{
if (versionBit >= VERSIONBITS_NUM_BITS) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-nbit-out-of-bounds");
Expand All @@ -62,7 +77,6 @@ bool MNHFTx::Verify(const uint256& quorumHash, const uint256& msgHash, TxValidat
const Consensus::LLMQType& llmqType = Params().GetConsensus().llmqTypeMnhf;
const auto quorum = llmq::quorumManager->GetQuorum(llmqType, quorumHash);

const uint256 requestId = ::SerializeHash(std::make_pair(MNEHF_REQUESTID_PREFIX, int64_t{versionBit}));
const uint256 signHash = llmq::utils::BuildSignHash(llmqType, quorum->qc->quorumHash, requestId, msgHash);
if (!sig.VerifyInsecure(quorum->qc->quorumPublicKey, signHash)) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-mnhf-invalid");
Expand Down Expand Up @@ -104,7 +118,7 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, TxValida
uint256 msgHash = tx_copy.GetHash();


if (!mnhfTx.signal.Verify(mnhfTx.signal.quorumHash, msgHash, state)) {
if (!mnhfTx.signal.Verify(mnhfTx.signal.quorumHash, mnhfTx.GetRequestId(), msgHash, state)) {
// set up inside Verify
return false;
}
Expand Down
15 changes: 13 additions & 2 deletions src/evo/mnhftx.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class MNHFTx
CBLSSignature sig{};

MNHFTx() = default;
bool Verify(const uint256& quorumHash, const uint256& msgHash, TxValidationState& state) const;
bool Verify(const uint256& quorumHash, const uint256& requestId, const uint256& msgHash, TxValidationState& state) const;

SERIALIZE_METHODS(MNHFTx, obj)
{
Expand Down Expand Up @@ -63,6 +63,17 @@ class MNHFTxPayload
uint8_t nVersion{CURRENT_VERSION};
MNHFTx signal;

public:
/**
* helper function to calculate Request ID used for signing
*/
uint256 GetRequestId() const;

/**
* helper function to prepare special transaction for signing
*/
CMutableTransaction PrepareTx() const;

SERIALIZE_METHODS(MNHFTxPayload, obj)
{
READWRITE(obj.nVersion, obj.signal);
Expand Down Expand Up @@ -120,6 +131,7 @@ class CMNHFManager
* This member function is not const because it calls non-const GetFromCache()
*/
Signals GetSignalsStage(const CBlockIndex* const pindexPrev);

private:
void AddToCache(const Signals& signals, const CBlockIndex* const pindex);

Expand All @@ -129,7 +141,6 @@ class CMNHFManager
* validate them by
*/
Signals GetFromCache(const CBlockIndex* const pindex);

};

std::optional<uint8_t> extractEHFSignal(const CTransaction& tx);
Expand Down
4 changes: 3 additions & 1 deletion src/llmq/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <llmq/commitment.h>
#include <llmq/debug.h>
#include <llmq/dkgsessionmgr.h>
#include <llmq/ehf_signals.h>
#include <llmq/instantsend.h>
#include <llmq/quorums.h>
#include <llmq/signing.h>
Expand Down Expand Up @@ -45,7 +46,8 @@ LLMQContext::LLMQContext(CChainState& chainstate, CConnman& connman, CEvoDB& evo
assert(llmq::quorumInstantSendManager == nullptr);
llmq::quorumInstantSendManager = std::make_unique<llmq::CInstantSendManager>(*llmq::chainLocksHandler, chainstate, connman, *llmq::quorumManager, *sigman, *shareman, sporkman, mempool, *::masternodeSync, peerman, unit_tests, wipe);
return llmq::quorumInstantSendManager.get();
}()}
}()},
ehfSignalsHandler{std::make_unique<llmq::CEHFSignalsHandler>(chainstate, connman, *sigman, *shareman, sporkman, *llmq::quorumManager, mempool)}
{
// NOTE: we use this only to wipe the old db, do NOT use it for anything else
// TODO: remove it in some future version
Expand Down
13 changes: 8 additions & 5 deletions src/llmq/context.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,27 @@ class CChainState;
class CConnman;
class CDBWrapper;
class CEvoDB;
class CTxMemPool;
class CSporkManager;
class CTxMemPool;
class PeerManager;

namespace llmq {
class CChainLocksHandler;
class CDKGDebugManager;
class CQuorumBlockProcessor;
class CDKGSessionManager;
class CEHFSignalsHandler;
class CInstantSendManager;
class CQuorumBlockProcessor;
class CQuorumManager;
class CSigSharesManager;
class CSigningManager;
class CChainLocksHandler;
class CInstantSendManager;
}

struct LLMQContext {
LLMQContext() = delete;
LLMQContext(const LLMQContext&) = delete;
LLMQContext(CChainState& chainstate, CConnman& connman, CEvoDB& evo_db, CSporkManager& sporkman, CTxMemPool& mempool,
LLMQContext(CChainState& chainstate, CConnman& connman, CEvoDB& evo_db, CSporkManager& sporkman,
CTxMemPool& mempool,
const std::unique_ptr<PeerManager>& peerman, bool unit_tests, bool wipe);
~LLMQContext();

Expand All @@ -57,6 +59,7 @@ struct LLMQContext {
const std::unique_ptr<llmq::CSigSharesManager> shareman;
llmq::CChainLocksHandler* const clhandler;
llmq::CInstantSendManager* const isman;
const std::unique_ptr<llmq::CEHFSignalsHandler> ehfSignalsHandler;
};

#endif // BITCOIN_LLMQ_CONTEXT_H
132 changes: 132 additions & 0 deletions src/llmq/ehf_signals.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright (c) 2023 The Dash Core developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <llmq/ehf_signals.h>
#include <llmq/utils.h>
#include <llmq/quorums.h>
#include <llmq/signing_shares.h>
#include <llmq/commitment.h>


#include <evo/mnhftx.h>
#include <evo/specialtx.h>

#include <index/txindex.h> // g_txindex

#include <primitives/transaction.h>
#include <spork.h>
#include <txmempool.h>
#include <validation.h>

namespace llmq {


CEHFSignalsHandler::CEHFSignalsHandler(CChainState& chainstate, CConnman& connman,
CSigningManager& sigman, CSigSharesManager& shareman,
const CSporkManager& sporkman, const CQuorumManager& qman, CTxMemPool& mempool) :
chainstate(chainstate),
connman(connman),
sigman(sigman),
shareman(shareman),
sporkman(sporkman),
qman(qman),
mempool(mempool)
{
sigman.RegisterRecoveredSigsListener(this);
}


CEHFSignalsHandler::~CEHFSignalsHandler()
{
sigman.UnregisterRecoveredSigsListener(this);
}

void CEHFSignalsHandler::UpdatedBlockTip(const CBlockIndex* const pindexNew)
{
if (!fMasternodeMode || !llmq::utils::IsV20Active(pindexNew) || !sporkman.IsSporkActive(SPORK_24_EHF)) {
return;
}

// TODO: should do this for all not-yet-signied bits
trySignEHFSignal(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_MN_RR].bit, pindexNew);
}

void CEHFSignalsHandler::trySignEHFSignal(int bit, const CBlockIndex* const pindex)
{
MNHFTxPayload mnhfPayload;
mnhfPayload.signal.versionBit = bit;
const uint256 requestId = mnhfPayload.GetRequestId();

LogPrintf("CEHFSignalsHandler::trySignEHFSignal: bit=%d at height=%d id=%s\n", bit, pindex->nHeight, requestId.ToString());

const Consensus::LLMQType& llmqType = Params().GetConsensus().llmqTypeMnhf;
const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType);
if (!llmq_params_opt.has_value()) {
return;
}
if (sigman.HasRecoveredSigForId(llmqType, requestId)) {
LOCK(cs);
ids.insert(requestId);

// no need to sign same message one more time
return;
}

const auto quorum = sigman.SelectQuorumForSigning(llmq_params_opt.value(), qman, requestId);
if (!quorum) {
LogPrintf("CEHFSignalsHandler::trySignEHFSignal no quorum for id=%s\n", requestId.ToString());
return;
}

mnhfPayload.signal.quorumHash = quorum->qc->quorumHash;
const uint256 msgHash = mnhfPayload.PrepareTx().GetHash();

{
LOCK(cs);
ids.insert(requestId);
}
sigman.AsyncSignIfMember(llmqType, shareman, requestId, msgHash);
}

void CEHFSignalsHandler::HandleNewRecoveredSig(const CRecoveredSig& recoveredSig)
{
if (g_txindex) {
g_txindex->BlockUntilSyncedToCurrentChain();
}

if (WITH_LOCK(cs, return ids.find(recoveredSig.getId()) == ids.end())) {
// Do nothing, it's not for this handler
return;
}

MNHFTxPayload mnhfPayload;
// TODO: should do this for all not-yet-signied bits
mnhfPayload.signal.versionBit = Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_MN_RR].bit;

const uint256 expectedId = mnhfPayload.GetRequestId();
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig expecting ID=%s received=%s\n", expectedId.ToString(), recoveredSig.getId().ToString());
if (recoveredSig.getId() != mnhfPayload.GetRequestId()) {
// there's nothing interesting for CEHFSignalsHandler
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig id is known but it's not MN_RR, expected: %s\n", mnhfPayload.GetRequestId().ToString());
return;
}

mnhfPayload.signal.quorumHash = recoveredSig.getQuorumHash();
mnhfPayload.signal.sig = recoveredSig.sig.Get();

CMutableTransaction tx = mnhfPayload.PrepareTx();

{
CTransactionRef tx_to_sent = MakeTransactionRef(std::move(tx));
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig Special EHF TX is created hash=%s\n", tx_to_sent->GetHash().ToString());
LOCK(cs_main);
TxValidationState state;
if (AcceptToMemoryPool(chainstate, mempool, state, tx_to_sent, /* bypass_limits=*/ false, /* nAbsurdFee=*/ 0)) {
connman.RelayTransaction(*tx_to_sent);
} else {
LogPrintf("CEHFSignalsHandler::HandleNewRecoveredSig -- AcceptToMemoryPool failed: %s\n", state.ToString());
}
}
}
} // namespace llmq
Loading