mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
feat: auto generation EHF and spork+EHF activation for MN_RR (#5597)
Implementation EHF mechanism, part 4. Previous changes are: - https://github.com/dashpay/dash/pull/4577 - https://github.com/dashpay/dash/pull/5505 - https://github.com/dashpay/dash/pull/5469 ## Issue being fixed or feature implemented Currently MN_RR is activated automatically by soft-fork activation after v20 is activated. It is not flexible enough, because platform may not be released by that time yet or in opposite it can be too long to wait. Also, any signal of EHF requires manual actions from MN owners to sign EHF signal - it is automated here. ## What was done? New spork `SPORK_24_MN_RR_READY`; new EHF manager that sign EHF signals semi-automatically without manual actions; and send transaction with EHF signal when signal is signed to network. Updated rpc `getblockchaininfo` to return information about of EHF activated forks. Fixed function `IsTxSafeForMining` in chainlock's handler to skip transactions without inputs (empty `vin`). ## How Has This Been Tested? Run unit/functional tests. Some tests have been updated due to new way of MN_RR activation: `feature_asset_locks.py`, `feature_mnehf.py`, `feature_llmq_evo.py` and unit test `block_reward_reallocation_tests`. ## Breaking Changes New way of MN_RR activation. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] 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)_ --------- Co-authored-by: UdjinM6 <UdjinM6@users.noreply.github.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
This commit is contained in:
parent
cecf63e0b7
commit
63ed462c54
@ -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 \
|
||||
|
@ -219,10 +219,11 @@ public:
|
||||
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
|
||||
@ -420,6 +421,7 @@ public:
|
||||
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
|
||||
@ -591,6 +593,7 @@ public:
|
||||
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");
|
||||
@ -826,10 +829,11 @@ public:
|
||||
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");
|
||||
|
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
@ -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");
|
||||
@ -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");
|
||||
@ -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;
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
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)
|
||||
{
|
||||
@ -63,6 +63,17 @@ public:
|
||||
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);
|
||||
@ -120,6 +131,7 @@ public:
|
||||
* 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);
|
||||
|
||||
@ -129,7 +141,6 @@ private:
|
||||
* validate them by
|
||||
*/
|
||||
Signals GetFromCache(const CBlockIndex* const pindex);
|
||||
|
||||
};
|
||||
|
||||
std::optional<uint8_t> extractEHFSignal(const CTransaction& tx);
|
||||
|
@ -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>
|
||||
@ -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
|
||||
|
@ -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();
|
||||
|
||||
@ -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
src/llmq/ehf_signals.cpp
Normal file
132
src/llmq/ehf_signals.cpp
Normal file
@ -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
|
61
src/llmq/ehf_signals.h
Normal file
61
src/llmq/ehf_signals.h
Normal file
@ -0,0 +1,61 @@
|
||||
// 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.
|
||||
|
||||
#ifndef BITCOIN_LLMQ_EHF_SIGNALS_H
|
||||
#define BITCOIN_LLMQ_EHF_SIGNALS_H
|
||||
|
||||
#include <llmq/signing.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
class CBlockIndex;
|
||||
class CChainState;
|
||||
class CConnman;
|
||||
class CSporkManager;
|
||||
class CTxMemPool;
|
||||
|
||||
namespace llmq
|
||||
{
|
||||
class CQuorumManager;
|
||||
class CSigSharesManager;
|
||||
class CSigningManager;
|
||||
|
||||
class CEHFSignalsHandler : public CRecoveredSigsListener
|
||||
{
|
||||
private:
|
||||
CChainState& chainstate;
|
||||
CConnman& connman;
|
||||
CSigningManager& sigman;
|
||||
CSigSharesManager& shareman;
|
||||
const CSporkManager& sporkman;
|
||||
const CQuorumManager& qman;
|
||||
CTxMemPool& mempool;
|
||||
|
||||
/**
|
||||
* keep freshly generated IDs for easier filter sigs in HandleNewRecoveredSig
|
||||
*/
|
||||
mutable Mutex cs;
|
||||
std::set<uint256> ids GUARDED_BY(cs);
|
||||
public:
|
||||
explicit CEHFSignalsHandler(CChainState& chainstate, CConnman& connman,
|
||||
CSigningManager& sigman, CSigSharesManager& shareman,
|
||||
const CSporkManager& sporkman, const CQuorumManager& qman, CTxMemPool& mempool);
|
||||
~CEHFSignalsHandler();
|
||||
|
||||
|
||||
/**
|
||||
* Since Tip is updated it could be a time to generate EHF Signal
|
||||
*/
|
||||
void UpdatedBlockTip(const CBlockIndex* const pindexNew);
|
||||
|
||||
void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig) override LOCKS_EXCLUDED(cs);
|
||||
|
||||
private:
|
||||
void trySignEHFSignal(int bit, const CBlockIndex* const pindex) LOCKS_EXCLUDED(cs);
|
||||
|
||||
};
|
||||
|
||||
} // namespace llmq
|
||||
|
||||
#endif // BITCOIN_LLMQ_EHF_SIGNALS_H
|
@ -311,7 +311,7 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa
|
||||
const auto& txid = it->GetTx().GetHash();
|
||||
if (!m_isman.RejectConflictingBlocks() || !m_isman.IsInstantSendEnabled() || m_isman.IsLocked(txid)) continue;
|
||||
|
||||
if (!m_clhandler.IsTxSafeForMining(txid)) {
|
||||
if (!it->GetTx().vin.empty() && !m_clhandler.IsTxSafeForMining(txid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1614,6 +1614,7 @@ static void BIP9SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniVal
|
||||
}
|
||||
bip9.pushKV("start_time", consensusParams.vDeployments[id].nStartTime);
|
||||
bip9.pushKV("timeout", consensusParams.vDeployments[id].nTimeout);
|
||||
bip9.pushKV("ehf", consensusParams.vDeployments[id].nMNActivationHeight);
|
||||
int64_t since_height = VersionBitsStateSinceHeight(active_chain_tip, consensusParams, id, versionbitscache);
|
||||
bip9.pushKV("since", since_height);
|
||||
if (ThresholdState::STARTED == thresholdState)
|
||||
|
@ -41,6 +41,7 @@ enum SporkId : int32_t {
|
||||
SPORK_19_CHAINLOCKS_ENABLED = 10018,
|
||||
SPORK_21_QUORUM_ALL_CONNECTED = 10020,
|
||||
SPORK_23_QUORUM_POSE = 10022,
|
||||
SPORK_24_EHF = 10023,
|
||||
|
||||
SPORK_INVALID = -1,
|
||||
};
|
||||
@ -66,7 +67,7 @@ struct CSporkDef
|
||||
};
|
||||
|
||||
#define MAKE_SPORK_DEF(name, defaultValue) CSporkDef{name, defaultValue, #name}
|
||||
[[maybe_unused]] static constexpr std::array<CSporkDef, 7> sporkDefs = {
|
||||
[[maybe_unused]] static constexpr std::array<CSporkDef, 8> sporkDefs = {
|
||||
MAKE_SPORK_DEF(SPORK_2_INSTANTSEND_ENABLED, 4070908800ULL), // OFF
|
||||
MAKE_SPORK_DEF(SPORK_3_INSTANTSEND_BLOCK_FILTERING, 4070908800ULL), // OFF
|
||||
MAKE_SPORK_DEF(SPORK_9_SUPERBLOCKS_ENABLED, 4070908800ULL), // OFF
|
||||
@ -74,6 +75,7 @@ struct CSporkDef
|
||||
MAKE_SPORK_DEF(SPORK_19_CHAINLOCKS_ENABLED, 4070908800ULL), // OFF
|
||||
MAKE_SPORK_DEF(SPORK_21_QUORUM_ALL_CONNECTED, 4070908800ULL), // OFF
|
||||
MAKE_SPORK_DEF(SPORK_23_QUORUM_POSE, 4070908800ULL), // OFF
|
||||
MAKE_SPORK_DEF(SPORK_24_EHF, 4070908800ULL), // OFF
|
||||
};
|
||||
#undef MAKE_SPORK_DEF
|
||||
extern std::unique_ptr<CSporkManager> sporkManager;
|
||||
|
@ -40,7 +40,7 @@ using SimpleUTXOMap = std::map<COutPoint, std::pair<int, CAmount>>;
|
||||
struct TestChainBRRBeforeActivationSetup : public TestChainSetup
|
||||
{
|
||||
// Force fast DIP3 activation
|
||||
TestChainBRRBeforeActivationSetup() : TestChainSetup(497, {"-dip3params=30:50"}) {}
|
||||
TestChainBRRBeforeActivationSetup() : TestChainSetup(497, {"-dip3params=30:50", "-vbparams=mn_rr:0:999999999999:20:16:12:5:0"}) {}
|
||||
};
|
||||
|
||||
static SimpleUTXOMap BuildSimpleUtxoMap(const std::vector<CTransactionRef>& txs)
|
||||
@ -265,6 +265,9 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS
|
||||
}
|
||||
BOOST_CHECK(!llmq::utils::IsMNRewardReallocationActive(m_node.chainman->ActiveChain().Tip()));
|
||||
|
||||
// Activate EHF "MN_RR"
|
||||
Params().UpdateMNActivationParam(Params().GetConsensus().vDeployments[Consensus::DEPLOYMENT_MN_RR].bit, ::ChainActive().Height(), ::ChainActive().Tip()->GetMedianTimePast(), /*fJustCheck=*/ false);
|
||||
|
||||
// Reward split should stay ~60/40 after reallocation is done,
|
||||
// check 10 next superblocks
|
||||
for ([[maybe_unused]] auto i : irange::range(10)) {
|
||||
|
@ -41,6 +41,7 @@ from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_greater_than,
|
||||
assert_greater_than_or_equal,
|
||||
get_bip9_details,
|
||||
hex_str_to_bytes,
|
||||
)
|
||||
|
||||
@ -83,7 +84,6 @@ class AssetLocksTest(DashTestFramework):
|
||||
lock_tx.vExtraPayload = lockTx_payload.serialize()
|
||||
|
||||
lock_tx = node_wallet.signrawtransactionwithwallet(lock_tx.serialize().hex())
|
||||
self.log.info(f"next tx: {lock_tx} payload: {lockTx_payload}")
|
||||
return FromHex(CTransaction(), lock_tx["hex"])
|
||||
|
||||
|
||||
@ -295,8 +295,8 @@ class AssetLocksTest(DashTestFramework):
|
||||
|
||||
self.log.info("Mine a quorum...")
|
||||
self.mine_quorum()
|
||||
self.validate_credit_pool_balance(locked_1)
|
||||
|
||||
self.validate_credit_pool_balance(locked_1)
|
||||
|
||||
self.log.info("Testing asset unlock...")
|
||||
|
||||
@ -428,8 +428,8 @@ class AssetLocksTest(DashTestFramework):
|
||||
self.log.info(f"Collecting coins in pool... Collected {total}/{10_900 * COIN}")
|
||||
coin = coins.pop()
|
||||
to_lock = int(coin['amount'] * COIN) - tiny_amount
|
||||
if to_lock > 50 * COIN:
|
||||
to_lock = 50 * COIN
|
||||
if to_lock > 99 * COIN:
|
||||
to_lock = 99 * COIN
|
||||
total += to_lock
|
||||
tx = self.create_assetlock(coin, to_lock, pubkey)
|
||||
self.send_tx_simple(tx)
|
||||
@ -455,6 +455,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
self.sync_mempools()
|
||||
node.generate(1)
|
||||
self.sync_all()
|
||||
self.log.info(f"MN_RR status: {get_bip9_details(node, 'mn_rr')}")
|
||||
|
||||
new_total = self.get_credit_pool_balance()
|
||||
amount_actually_withdrawn = total - new_total
|
||||
@ -499,14 +500,17 @@ class AssetLocksTest(DashTestFramework):
|
||||
node.generate(1)
|
||||
self.sync_all()
|
||||
|
||||
self.log.info("generate many blocks to be sure that mempool is empty afterwards...")
|
||||
self.log.info("generate many blocks to be sure that mempool is empty after expiring txes...")
|
||||
self.slowly_generate_batch(60)
|
||||
self.log.info("Checking that credit pool is not changed...")
|
||||
assert_equal(new_total, self.get_credit_pool_balance())
|
||||
self.check_mempool_size()
|
||||
|
||||
self.activate_mn_rr(expected_activation_height=3090)
|
||||
# activate MN_RR reallocation
|
||||
self.activate_mn_rr(expected_activation_height=node.getblockcount() + 12 * 3)
|
||||
self.log.info(f'height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}')
|
||||
assert_equal(new_total, self.get_credit_pool_balance())
|
||||
|
||||
bt = node.getblocktemplate()
|
||||
platform_reward = bt['masternode'][0]['amount']
|
||||
assert_equal(bt['masternode'][0]['script'], '6a') # empty OP_RETURN
|
||||
@ -515,7 +519,7 @@ class AssetLocksTest(DashTestFramework):
|
||||
all_mn_rewards = platform_reward + owner_reward + operator_reward
|
||||
assert_equal(all_mn_rewards, bt['coinbasevalue'] * 3 // 4) # 75/25 mn/miner reward split
|
||||
assert_equal(platform_reward, all_mn_rewards * 375 // 1000) # 0.375 platform share
|
||||
assert_equal(platform_reward, 25553999)
|
||||
assert_equal(platform_reward, 29636590)
|
||||
assert_equal(new_total, self.get_credit_pool_balance())
|
||||
node.generate(1)
|
||||
self.sync_all()
|
||||
|
@ -114,6 +114,7 @@ class LLMQEvoNodesTest(DashTestFramework):
|
||||
self.log.info("Test that EvoNodes are paid 4x blocks in a row")
|
||||
self.test_evo_payments(window_analysis=256)
|
||||
|
||||
self.activate_v20()
|
||||
self.activate_mn_rr()
|
||||
self.log.info("Activated MN RewardReallocation at height:" + str(self.nodes[0].getblockcount()))
|
||||
|
||||
|
@ -160,11 +160,16 @@ class MnehfTest(DashTestFramework):
|
||||
assert_equal(get_bip9_details(node, 'testdummy')['status'], 'defined')
|
||||
self.activate_v20()
|
||||
assert_equal(get_bip9_details(node, 'testdummy')['status'], 'defined')
|
||||
assert_equal(get_bip9_details(node, 'mn_rr')['status'], 'defined')
|
||||
|
||||
ehf_tx_sent = self.send_tx(ehf_tx)
|
||||
self.log.info(f"ehf tx: {ehf_tx_sent}")
|
||||
ehf_unknown_tx_sent = self.send_tx(ehf_unknown_tx)
|
||||
self.log.info(f"unknown ehf tx: {ehf_unknown_tx_sent}")
|
||||
self.send_tx(ehf_invalid_tx, expected_error='bad-mnhf-non-ehf')
|
||||
ehf_blockhash = node.generate(1)[0]
|
||||
self.sync_all()
|
||||
ehf_blockhash = self.nodes[1].generate(1)[0]
|
||||
self.sync_blocks()
|
||||
self.sync_all()
|
||||
|
||||
self.log.info(f"Check MnEhfTx {ehf_tx_sent} was mined in {ehf_blockhash}")
|
||||
@ -251,6 +256,12 @@ class MnehfTest(DashTestFramework):
|
||||
self.mine_quorum()
|
||||
|
||||
ehf_tx_new_start = self.create_mnehf(28, pubkey)
|
||||
|
||||
self.log.info("activate MN_RR also by enabling spork 24")
|
||||
assert_equal(get_bip9_details(node, 'mn_rr')['status'], 'defined')
|
||||
self.nodes[0].sporkupdate("SPORK_24_EHF", 0)
|
||||
self.wait_for_sporks_same()
|
||||
|
||||
self.check_fork('defined')
|
||||
|
||||
self.log.info("Mine one block and ensure EHF tx for the new deployment is mined")
|
||||
@ -263,6 +274,8 @@ class MnehfTest(DashTestFramework):
|
||||
self.check_fork('defined')
|
||||
self.slowly_generate_batch(12 * 4)
|
||||
self.check_fork('active')
|
||||
self.log.info(f"bip9: {get_bip9_details(node, 'mn_rr')}")
|
||||
assert_equal(get_bip9_details(node, 'mn_rr')['status'], 'active')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -146,7 +146,8 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
'status': 'defined',
|
||||
'start_time': 0,
|
||||
'timeout': 9223372036854775807,
|
||||
'since': 0
|
||||
'since': 0,
|
||||
'ehf': -1,
|
||||
}, 'active': False},
|
||||
'mn_rr': {
|
||||
'type': 'bip9',
|
||||
@ -154,7 +155,8 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
'status': 'defined',
|
||||
'start_time': 0,
|
||||
'timeout': 9223372036854775807,
|
||||
'since': 0
|
||||
'since': 0,
|
||||
'ehf': 0,
|
||||
},
|
||||
'active': False},
|
||||
'testdummy': {
|
||||
@ -172,6 +174,7 @@ class BlockchainTest(BitcoinTestFramework):
|
||||
'count': 57,
|
||||
'possible': True,
|
||||
},
|
||||
'ehf': -1,
|
||||
},
|
||||
'active': False},
|
||||
})
|
||||
|
@ -44,6 +44,7 @@ from .util import (
|
||||
check_json_precision,
|
||||
copy_datadir,
|
||||
force_finish_mnsync,
|
||||
get_bip9_details,
|
||||
get_datadir_path,
|
||||
hex_str_to_bytes,
|
||||
initialize_datadir,
|
||||
@ -1070,6 +1071,7 @@ class DashTestFramework(BitcoinTestFramework):
|
||||
self.sync_blocks()
|
||||
|
||||
def activate_by_name(self, name, expected_activation_height=None):
|
||||
assert not softfork_active(self.nodes[0], name)
|
||||
self.log.info("Wait for " + name + " activation")
|
||||
|
||||
# disable spork17 while mining blocks to activate "name" to prevent accidental quorum formation
|
||||
@ -1082,6 +1084,7 @@ class DashTestFramework(BitcoinTestFramework):
|
||||
batch_size = 10
|
||||
if expected_activation_height is not None:
|
||||
height = self.nodes[0].getblockcount()
|
||||
assert height < expected_activation_height
|
||||
# NOTE: getblockchaininfo shows softforks active at block (window * 3 - 1)
|
||||
# since it's returning whether a softwork is active for the _next_ block.
|
||||
# Hence the last block prior to the activation is (expected_activation_height - 2).
|
||||
@ -1122,6 +1125,14 @@ class DashTestFramework(BitcoinTestFramework):
|
||||
self.activate_by_name('v20', expected_activation_height)
|
||||
|
||||
def activate_mn_rr(self, expected_activation_height=None):
|
||||
self.nodes[0].sporkupdate("SPORK_24_EHF", 0)
|
||||
self.wait_for_sporks_same()
|
||||
mn_rr_status = 0
|
||||
while mn_rr_status == 0:
|
||||
time.sleep(1)
|
||||
mn_rr_status = get_bip9_details(self.nodes[0], 'mn_rr')['ehf']
|
||||
self.nodes[0].generate(1)
|
||||
self.sync_all()
|
||||
self.activate_by_name('mn_rr', expected_activation_height)
|
||||
|
||||
def set_dash_llmq_test_params(self, llmq_size, llmq_threshold):
|
||||
|
Loading…
Reference in New Issue
Block a user