Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
4 changes: 3 additions & 1 deletion .github/workflows/release_docker_hub.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ jobs:
uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v2
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:qemu-v7.0.0-28

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
Expand Down
6 changes: 3 additions & 3 deletions COPYING
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
The MIT License (MIT)

Copyright (c) 2009-2024 The Bitcoin Core developers
Copyright (c) 2009-2024 Bitcoin Developers
Copyright (c) 2014-2024 The Dash Core developers
Copyright (c) 2009-2025 The Bitcoin Core developers
Copyright (c) 2009-2025 Bitcoin Developers
Copyright (c) 2014-2025 The Dash Core developers

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions contrib/debian/copyright
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ Upstream-Contact: Dash Core Group, Inc https://www.dash.org/dcg/
Source: https://github.com/dashpay/dash

Files: *
Copyright: 2009-2024, Bitcoin Core Developers,
2019-2024, Dash Core Developers
Copyright: 2009-2025, Bitcoin Core Developers,
2019-2025, Dash Core Developers
License: Expat
Comment: The Bitcoin Core Developers encompasses the current developers listed on bitcoin.org,
as well as the numerous contributors to the project. The Dash Core Developers
Expand Down
4 changes: 4 additions & 0 deletions doc/release-notes-6608.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
P2P Changes
-----------

* `cycleHash` field in `isdlock` message will now represent a DKG cycle starting block of the signing quorum instead of a DKG cycle starting block corresponding to the current chain height. While this is fully backwards compatible with older versions of Dash Core, other implementations might not be expecting this, so the P2P protocol version was bumped to 70237.
11 changes: 10 additions & 1 deletion src/bls/bls.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,7 @@ class CBLSLazyWrapper
mutable std::mutex mutex;

mutable std::array<uint8_t, BLSObject::SerSize> vecBytes;
// Indicates if the value contained in vecBytes is valid
mutable bool bufValid{false};
mutable bool bufLegacyScheme{true};

Expand Down Expand Up @@ -462,7 +463,7 @@ class CBLSLazyWrapper
{
std::unique_lock<std::mutex> l(mutex);
s.read(AsWritableBytes(Span{vecBytes.data(), BLSObject::SerSize}));
bufValid = true;
bufValid = std::any_of(vecBytes.begin(), vecBytes.end(), [](uint8_t c) { return c != 0; });
bufLegacyScheme = specificLegacyScheme;
objInitialized = false;
hash.SetNull();
Expand Down Expand Up @@ -507,6 +508,14 @@ class CBLSLazyWrapper

bool operator==(const CBLSLazyWrapper& r) const
{
// If neither bufValid or objInitialized are set, then the object is the default object.
const bool is_default{!bufValid && !objInitialized};
const bool r_is_default{!r.bufValid && !r.objInitialized};
// If both are default; they are equal.
if (is_default && r_is_default) return true;
// If one is default and the other isn't, we are not equal
if (is_default != r_is_default) return false;

if (bufValid && r.bufValid && bufLegacyScheme == r.bufLegacyScheme) {
return vecBytes == r.vecBytes;
}
Expand Down
8 changes: 5 additions & 3 deletions src/evo/deterministicmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate keyIDOwner=%s", __func__,
dmn->proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner)))));
}
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
if (dmn->pdmnState->pubKeyOperator != CBLSLazyPublicKey() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__,
dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
Expand Down Expand Up @@ -578,7 +578,8 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash)
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a keyIDOwner=%s", __func__,
proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner)))));
}
if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
if (dmn->pdmnState->pubKeyOperator != CBLSLazyPublicKey() &&
!DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) {
mnUniquePropertyMap = mnUniquePropertyMapSaved;
throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a pubKeyOperator=%s", __func__,
proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString())));
Expand Down Expand Up @@ -840,7 +841,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, gsl::no
}
if (newState->IsBanned()) {
// only revive when all keys are set
if (newState->pubKeyOperator.Get().IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) {
if (newState->pubKeyOperator != CBLSLazyPublicKey() && !newState->keyIDVoting.IsNull() &&
!newState->keyIDOwner.IsNull()) {
newState->Revive(nHeight);
if (debugLogs) {
LogPrintf("CDeterministicMNManager::%s -- MN %s revived at height %d\n",
Expand Down
3 changes: 2 additions & 1 deletion src/evo/simplifiedmns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ UniValue CSimplifiedMNListDiff::ToJson(bool extended) const
}
obj.pushKV("newQuorums", newQuorumsArr);

if (const auto opt_cbTxPayload = GetTxPayload<CCbTx>(*cbTx)) {
// Do not assert special tx type here since this can be called prior to DIP0003 activation
if (const auto opt_cbTxPayload = GetTxPayload<CCbTx>(*cbTx, /*assert_type=*/false)) {
obj.pushKV("merkleRootMNList", opt_cbTxPayload->merkleRootMNList.ToString());
if (opt_cbTxPayload->nVersion >= CCbTx::Version::MERKLE_ROOT_QUORUMS) {
obj.pushKV("merkleRootQuorums", opt_cbTxPayload->merkleRootQuorums.ToString());
Expand Down
25 changes: 15 additions & 10 deletions src/llmq/instantsend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -671,21 +671,26 @@ void CInstantSendManager::TrySignInstantSendLock(const CTransaction& tx)
islock.inputs.emplace_back(in.prevout);
}

{
const auto &llmq_params_opt = Params().GetLLMQ(llmqType);
assert(llmq_params_opt);
LOCK(cs_main);
const auto dkgInterval = llmq_params_opt->dkgInterval;
const auto quorumHeight = m_chainstate.m_chain.Height() - (m_chainstate.m_chain.Height() % dkgInterval);
islock.cycleHash = m_chainstate.m_chain[quorumHeight]->GetBlockHash();
}

auto id = islock.GetRequestId();

if (sigman.HasRecoveredSigForId(llmqType, id)) {
return;
}

const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
assert(llmq_params_opt);
const auto quorum = SelectQuorumForSigning(llmq_params_opt.value(), m_chainstate.m_chain, qman, id);

if (!quorum) {
LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- failed to select quorum. islock id=%s, txid=%s\n",
__func__, id.ToString(), tx.GetHash().ToString());
return;
}

const int cycle_height = quorum->m_quorum_base_block_index->nHeight -
quorum->m_quorum_base_block_index->nHeight % llmq_params_opt->dkgInterval;
islock.cycleHash = quorum->m_quorum_base_block_index->GetAncestor(cycle_height)->GetBlockHash();

{
LOCK(cs_creating);
auto e = creatingInstantSendLocks.emplace(id, std::move(islock));
Expand All @@ -695,7 +700,7 @@ void CInstantSendManager::TrySignInstantSendLock(const CTransaction& tx)
txToCreatingInstantSendLocks.emplace(tx.GetHash(), &e.first->second);
}

sigman.AsyncSignIfMember(llmqType, shareman, id, tx.GetHash());
sigman.AsyncSignIfMember(llmqType, shareman, id, tx.GetHash(), quorum->m_quorum_base_block_index->GetBlockHash());
}

void CInstantSendManager::HandleNewInstantSendLockRecoveredSig(const llmq::CRecoveredSig& recoveredSig)
Expand Down
125 changes: 99 additions & 26 deletions src/llmq/snapshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ UniValue CQuorumRotationInfo::ToJson() const
bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
const ChainstateManager& chainman, const CQuorumManager& qman,
const CQuorumBlockProcessor& qblockman, const CGetQuorumRotationInfo& request,
CQuorumRotationInfo& response, std::string& errorRet)
bool use_legacy_construction, CQuorumRotationInfo& response, std::string& errorRet)
{
AssertLockHeld(cs_main);

Expand All @@ -112,19 +112,23 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
}
baseBlockIndexes.push_back(blockIndex);
}
std::sort(baseBlockIndexes.begin(), baseBlockIndexes.end(), [](const CBlockIndex* a, const CBlockIndex* b) {
return a->nHeight < b->nHeight;
});
if (use_legacy_construction) {
std::sort(baseBlockIndexes.begin(), baseBlockIndexes.end(),
[](const CBlockIndex* a, const CBlockIndex* b) { return a->nHeight < b->nHeight; });
}
}

const CBlockIndex* tipBlockIndex = chainman.ActiveChain().Tip();
if (!tipBlockIndex) {
errorRet = strprintf("tip block not found");
return false;
}
//Build MN list Diff always with highest baseblock
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, baseBlockIndexes.back()->GetBlockHash(), tipBlockIndex->GetBlockHash(), response.mnListDiffTip, errorRet)) {
return false;
if (use_legacy_construction) {
// Build MN list Diff always with highest baseblock
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, baseBlockIndexes.back()->GetBlockHash(),
tipBlockIndex->GetBlockHash(), response.mnListDiffTip, errorRet)) {
return false;
}
}

const CBlockIndex* blockIndex = chainman.m_blockman.LookupBlockIndex(request.blockRequestHash);
Expand All @@ -149,15 +153,19 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
return false;
}

const CBlockIndex* pWorkBlockIndex = hBlockIndex->GetAncestor(hBlockIndex->nHeight - workDiff);
if (!pWorkBlockIndex) {
const CBlockIndex* pWorkBlockHIndex = hBlockIndex->GetAncestor(hBlockIndex->nHeight - workDiff);
if (!pWorkBlockHIndex) {
errorRet = strprintf("Can not find work block H");
return false;
}

//Build MN list Diff always with highest baseblock
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockIndex), pWorkBlockIndex->GetBlockHash(), response.mnListDiffH, errorRet)) {
return false;
if (use_legacy_construction) {
// Build MN list Diff always with highest baseblock
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHIndex, use_legacy_construction),
pWorkBlockHIndex->GetBlockHash(), response.mnListDiffH, errorRet)) {
return false;
}
}

const CBlockIndex* pBlockHMinusCIndex = tipBlockIndex->GetAncestor(hBlockIndex->nHeight - cycleLength);
Expand Down Expand Up @@ -202,8 +210,12 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
const CBlockIndex* pWorkBlockHMinus4CIndex = pBlockHMinus4CIndex->GetAncestor(pBlockHMinus4CIndex->nHeight - workDiff);
//Checked later if extraShare is on

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinusCIndex), pWorkBlockHMinusCIndex->GetBlockHash(), response.mnListDiffAtHMinusC, errorRet)) {
return false;
if (use_legacy_construction) {
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinusCIndex, use_legacy_construction),
pWorkBlockHMinusCIndex->GetBlockHash(), response.mnListDiffAtHMinusC, errorRet)) {
return false;
}
}

auto snapshotHMinusC = qsnapman.GetSnapshotForBlock(llmqType, pBlockHMinusCIndex);
Expand All @@ -214,8 +226,13 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
response.quorumSnapshotAtHMinusC = std::move(snapshotHMinusC.value());
}

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus2CIndex), pWorkBlockHMinus2CIndex->GetBlockHash(), response.mnListDiffAtHMinus2C, errorRet)) {
return false;
if (use_legacy_construction) {
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus2CIndex,
use_legacy_construction),
pWorkBlockHMinus2CIndex->GetBlockHash(), response.mnListDiffAtHMinus2C, errorRet)) {
return false;
}
}

auto snapshotHMinus2C = qsnapman.GetSnapshotForBlock(llmqType, pBlockHMinus2CIndex);
Expand All @@ -226,8 +243,13 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
response.quorumSnapshotAtHMinus2C = std::move(snapshotHMinus2C.value());
}

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus3CIndex), pWorkBlockHMinus3CIndex->GetBlockHash(), response.mnListDiffAtHMinus3C, errorRet)) {
return false;
if (use_legacy_construction) {
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus3CIndex,
use_legacy_construction),
pWorkBlockHMinus3CIndex->GetBlockHash(), response.mnListDiffAtHMinus3C, errorRet)) {
return false;
}
}

auto snapshotHMinus3C = qsnapman.GetSnapshotForBlock(llmqType, pBlockHMinus3CIndex);
Expand Down Expand Up @@ -255,10 +277,15 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
}

CSimplifiedMNListDiff mn4c;
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus4CIndex), pWorkBlockHMinus4CIndex->GetBlockHash(), mn4c, errorRet)) {
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus4CIndex,
use_legacy_construction),
pWorkBlockHMinus4CIndex->GetBlockHash(), mn4c, errorRet)) {
return false;
}

if (!use_legacy_construction) {
baseBlockIndexes.push_back(pWorkBlockHMinus4CIndex);
}
response.mnListDiffAtHMinus4C = std::move(mn4c);
} else {
response.extraShare = false;
Expand Down Expand Up @@ -311,22 +338,68 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan
}

CSimplifiedMNListDiff mnhneeded;
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman, GetLastBaseBlockHash(baseBlockIndexes, pNeededWorkBlockIndex), pNeededWorkBlockIndex->GetBlockHash(), mnhneeded, errorRet)) {
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pNeededWorkBlockIndex, use_legacy_construction),
pNeededWorkBlockIndex->GetBlockHash(), mnhneeded, errorRet)) {
return false;
}

if (!use_legacy_construction) {
baseBlockIndexes.push_back(pNeededWorkBlockIndex);
}
response.mnListDiffList.push_back(mnhneeded);
}

if (!use_legacy_construction) {
if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus3CIndex,
use_legacy_construction),
pWorkBlockHMinus3CIndex->GetBlockHash(), response.mnListDiffAtHMinus3C, errorRet)) {
return false;
}
baseBlockIndexes.push_back(pWorkBlockHMinus3CIndex);

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinus2CIndex,
use_legacy_construction),
pWorkBlockHMinus2CIndex->GetBlockHash(), response.mnListDiffAtHMinus2C, errorRet)) {
return false;
}
baseBlockIndexes.push_back(pWorkBlockHMinus2CIndex);

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHMinusCIndex, use_legacy_construction),
pWorkBlockHMinusCIndex->GetBlockHash(), response.mnListDiffAtHMinusC, errorRet)) {
return false;
}
baseBlockIndexes.push_back(pWorkBlockHMinusCIndex);

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, pWorkBlockHIndex, use_legacy_construction),
pWorkBlockHIndex->GetBlockHash(), response.mnListDiffH, errorRet)) {
return false;
}
baseBlockIndexes.push_back(pWorkBlockHIndex);

if (!BuildSimplifiedMNListDiff(dmnman, chainman, qblockman, qman,
GetLastBaseBlockHash(baseBlockIndexes, tipBlockIndex, use_legacy_construction),
tipBlockIndex->GetBlockHash(), response.mnListDiffTip, errorRet)) {
return false;
}
}
return true;
}

uint256 GetLastBaseBlockHash(Span<const CBlockIndex*> baseBlockIndexes, const CBlockIndex* blockIndex)
uint256 GetLastBaseBlockHash(Span<const CBlockIndex*> baseBlockIndexes, const CBlockIndex* blockIndex,
bool use_legacy_construction)
{
uint256 hash;
if (!use_legacy_construction) {
std::sort(baseBlockIndexes.begin(), baseBlockIndexes.end(),
[](const CBlockIndex* a, const CBlockIndex* b) { return a->nHeight < b->nHeight; });
}
// default to genesis block
uint256 hash{Params().GenesisBlock().GetHash()};
for (const auto baseBlock : baseBlockIndexes) {
if (baseBlock->nHeight >= blockIndex->nHeight)
break;
if (baseBlock->nHeight > blockIndex->nHeight) break;
hash = baseBlock->GetBlockHash();
}
return hash;
Expand Down
6 changes: 4 additions & 2 deletions src/llmq/snapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,10 @@ class CQuorumRotationInfo
bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman,
const ChainstateManager& chainman, const CQuorumManager& qman,
const CQuorumBlockProcessor& qblockman, const CGetQuorumRotationInfo& request,
CQuorumRotationInfo& response, std::string& errorRet) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
uint256 GetLastBaseBlockHash(Span<const CBlockIndex*> baseBlockIndexes, const CBlockIndex* blockIndex);
bool use_legacy_construction, CQuorumRotationInfo& response, std::string& errorRet)
EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
uint256 GetLastBaseBlockHash(Span<const CBlockIndex*> baseBlockIndexes, const CBlockIndex* blockIndex,
bool use_legacy_construction);

class CQuorumSnapshotManager
{
Expand Down
Loading