diff --git a/.github/workflows/release_docker_hub.yml b/.github/workflows/release_docker_hub.yml index bbdf7551928b..f4e1775a7859 100644 --- a/.github/workflows/release_docker_hub.yml +++ b/.github/workflows/release_docker_hub.yml @@ -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 diff --git a/COPYING b/COPYING index 9716a1f37bd2..f62ddd4725e2 100644 --- a/COPYING +++ b/COPYING @@ -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 diff --git a/configure.ac b/configure.ac index 0db473ff60a2..c380db5afb0d 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ AC_PREREQ([2.69]) dnl Don't forget to push a corresponding tag when updating any of _CLIENT_VERSION_* numbers define(_CLIENT_VERSION_MAJOR, 22) define(_CLIENT_VERSION_MINOR, 1) -define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_BUILD, 2) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2025) define(_COPYRIGHT_HOLDERS,[The %s developers]) diff --git a/contrib/debian/copyright b/contrib/debian/copyright index 87742b02a8f8..10a68e74ac75 100644 --- a/contrib/debian/copyright +++ b/contrib/debian/copyright @@ -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 diff --git a/doc/release-notes.md b/doc/release-notes.md index b7dc1df913aa..e7b615d54811 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,6 +1,6 @@ -# Dash Core version v22.1.1 +# Dash Core version v22.1.2 -This is a new minor version release, bringing various bugfixes. +This is a new minor version release, bringing various bugfixes and performance improvements. This release is **optional** for all nodes, although recommended. Please report bugs using the issue tracker at GitHub: @@ -26,18 +26,39 @@ likely require a reindex. # Release Notes -v2 P2P Downgrade Issues ------------------------ +Quorum Rotation Improvements +---------------------------- -This version addressed a problem affecting certain Dash-specific connection types, including mixing and masternode probes, when -downgrading from v2 to v1 connections. This resulted in increased connection count and load for masternodes. (dash#6574) +- Optimized `quorum rotationinfo` RPC and `GETQUORUMROTATIONINFO` P2P message by constructing diffs progressively from oldest to newest, reducing redundancy and improving efficiency (dash#6622). +- Fixed incorrect `baseBlockHash` handling, eliminating unnecessary quorum changes in responses and improving result accuracy and compactness (dash#6625). -Minor QT Coinjoin Fixes +Deployment and CI Fixes ----------------------- -- Avoid leaking CJ related balances in discrete mode (dash#6566) +- Pinned QEMU version to avoid segmentation faults during container builds (dash#6586). + +Performance Improvements +------------------------ + +- Improved the performance of deterministic masternode list management, significantly speeding up RPC calls such as `protx diff` (dash#6581). + +Coinjoin Tests Stability +------------------------ + +- Resolved potential deadlock in `coinjoin_tests.cpp` by ensuring wallet transaction scans occur outside critical wallet lock scope (dash#6593). + +Minor Build and Test Fixes +-------------------------- + +- Fixed assertion edge case for coinbase transactions (cbtx) in simplified masternode list diff outputs, specifically affecting debug builds (dash#6585). +- Updated copyright notices to 2025 in COPYING file and Debian packaging metadata (dash#6599). + +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. (#6608) -# v22.1.1 Change log +# v22.1.2 Change log See detailed [set of changes][set-of-changes]. @@ -45,6 +66,9 @@ See detailed [set of changes][set-of-changes]. Thanks to everyone who directly contributed to this release: +- Kittywhiskers Van Gogh +- Konstantin Akimov +- Odysseas Gabrielides - PastaPastaPasta - UdjinM6 @@ -55,6 +79,7 @@ debug the release candidates. These releases are considered obsolete. Old release notes can be found here: +- [v22.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.1.md) released Feb/17/2025 - [v22.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.0.md) released Feb/10/2025 - [v22.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.0.0.md) released Dec/12/2024 - [v21.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.1.1.md) released Oct/22/2024 @@ -110,4 +135,4 @@ These releases are considered obsolete. Old release notes can be found here: - [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 - [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 -[set-of-changes]: https://github.com/dashpay/dash/compare/v22.1.0...dashpay:v22.1.1 +[set-of-changes]: https://github.com/dashpay/dash/compare/v22.1.1...dashpay:v22.1.2 diff --git a/doc/release-notes/dash/release-notes-22.1.1.md b/doc/release-notes/dash/release-notes-22.1.1.md new file mode 100644 index 000000000000..b7dc1df913aa --- /dev/null +++ b/doc/release-notes/dash/release-notes-22.1.1.md @@ -0,0 +1,113 @@ +# Dash Core version v22.1.1 + +This is a new minor version release, bringing various bugfixes. +This release is **optional** for all nodes, although recommended. + +Please report bugs using the issue tracker at GitHub: + + + + +# Upgrading and downgrading + +## How to Upgrade + +If you are running an older version, shut it down. Wait until it has completely +shut down (which might take a few minutes for older versions), then run the +installer (on Windows) or just copy over /Applications/Dash-Qt (on Mac) or +dashd/dash-qt (on Linux). + +## Downgrade warning + +### Downgrade to a version < v22.0.0 + +Downgrading to a version older than v22.0.0 may not be supported, and will +likely require a reindex. + +# Release Notes + +v2 P2P Downgrade Issues +----------------------- + +This version addressed a problem affecting certain Dash-specific connection types, including mixing and masternode probes, when +downgrading from v2 to v1 connections. This resulted in increased connection count and load for masternodes. (dash#6574) + +Minor QT Coinjoin Fixes +----------------------- + +- Avoid leaking CJ related balances in discrete mode (dash#6566) + +# v22.1.1 Change log + +See detailed [set of changes][set-of-changes]. + +# Credits + +Thanks to everyone who directly contributed to this release: + +- PastaPastaPasta +- UdjinM6 + +As well as everyone that submitted issues, reviewed pull requests and helped +debug the release candidates. + +# Older releases + +These releases are considered obsolete. Old release notes can be found here: + +- [v22.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.1.0.md) released Feb/10/2025 +- [v22.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-22.0.0.md) released Dec/12/2024 +- [v21.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.1.1.md) released Oct/22/2024 +- [v21.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.1.0.md) released Aug/8/2024 +- [v21.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.2.md) released Aug/1/2024 +- [v21.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-21.0.0.md) released Jul/25/2024 +- [v20.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.1.md) released April/3/2024 +- [v20.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.1.0.md) released March/5/2024 +- [v20.0.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.4.md) released Jan/13/2024 +- [v20.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.3.md) released December/26/2023 +- [v20.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.2.md) released December/06/2023 +- [v20.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.1.md) released November/18/2023 +- [v20.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-20.0.0.md) released November/15/2023 +- [v19.3.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.3.0.md) released July/31/2023 +- [v19.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.2.0.md) released June/19/2023 +- [v19.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.1.0.md) released May/22/2023 +- [v19.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-19.0.0.md) released Apr/14/2023 +- [v18.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.2.md) released Mar/21/2023 +- [v18.2.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.1.md) released Jan/17/2023 +- [v18.2.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.0.md) released Jan/01/2023 +- [v18.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.1.md) released January/08/2023 +- [v18.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.1.0.md) released October/09/2022 +- [v18.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.2.md) released October/09/2022 +- [v18.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.0.1.md) released August/17/2022 +- [v0.17.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.3.md) released June/07/2021 +- [v0.17.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.17.0.2.md) released May/19/2021 +- [v0.16.1.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.1.md) released November/17/2020 +- [v0.16.1.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.1.0.md) released November/14/2020 +- [v0.16.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.16.0.1.md) released September/30/2020 +- [v0.15.0.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.15.0.0.md) released Febrary/18/2020 +- [v0.14.0.5](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.5.md) released December/08/2019 +- [v0.14.0.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.4.md) released November/22/2019 +- [v0.14.0.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.3.md) released August/15/2019 +- [v0.14.0.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.2.md) released July/4/2019 +- [v0.14.0.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.1.md) released May/31/2019 +- [v0.14.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.14.0.md) released May/22/2019 +- [v0.13.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.3.md) released Apr/04/2019 +- [v0.13.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.2.md) released Mar/15/2019 +- [v0.13.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.1.md) released Feb/9/2019 +- [v0.13.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.13.0.md) released Jan/14/2019 +- [v0.12.3.4](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.4.md) released Dec/14/2018 +- [v0.12.3.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.3.md) released Sep/19/2018 +- [v0.12.3.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.2.md) released Jul/09/2018 +- [v0.12.3.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.3.1.md) released Jul/03/2018 +- [v0.12.2.3](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.3.md) released Jan/12/2018 +- [v0.12.2.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.2.md) released Dec/17/2017 +- [v0.12.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.2.md) released Nov/08/2017 +- [v0.12.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.1.md) released Feb/06/2017 +- [v0.12.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.12.0.md) released Aug/15/2015 +- [v0.11.2](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.2.md) released Mar/04/2015 +- [v0.11.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.1.md) released Feb/10/2015 +- [v0.11.0](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.11.0.md) released Jan/15/2015 +- [v0.10.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.10.0.md) released Sep/25/2014 +- [v0.9.x](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-0.9.0.md) released Mar/13/2014 + +[set-of-changes]: https://github.com/dashpay/dash/compare/v22.1.0...dashpay:v22.1.1 diff --git a/src/bls/bls.h b/src/bls/bls.h index a81d9776d21d..0b0d81f74934 100644 --- a/src/bls/bls.h +++ b/src/bls/bls.h @@ -391,6 +391,7 @@ class CBLSLazyWrapper mutable std::mutex mutex; mutable std::array vecBytes; + // Indicates if the value contained in vecBytes is valid mutable bool bufValid{false}; mutable bool bufLegacyScheme{true}; @@ -462,7 +463,7 @@ class CBLSLazyWrapper { std::unique_lock 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(); @@ -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; } diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 13919caac1da..5a5acef5cfbb 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -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()))); @@ -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()))); @@ -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", diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 4695d55c41b8..c1acc31ea7c8 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -279,7 +279,8 @@ UniValue CSimplifiedMNListDiff::ToJson(bool extended) const } obj.pushKV("newQuorums", newQuorumsArr); - if (const auto opt_cbTxPayload = GetTxPayload(*cbTx)) { + // Do not assert special tx type here since this can be called prior to DIP0003 activation + if (const auto opt_cbTxPayload = GetTxPayload(*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()); diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp index 0e22ff05d42b..c4e02f373dc9 100644 --- a/src/llmq/instantsend.cpp +++ b/src/llmq/instantsend.cpp @@ -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)); @@ -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) diff --git a/src/llmq/snapshot.cpp b/src/llmq/snapshot.cpp index d1777916de42..52225ed620a5 100644 --- a/src/llmq/snapshot.cpp +++ b/src/llmq/snapshot.cpp @@ -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); @@ -112,9 +112,10 @@ 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(); @@ -122,9 +123,12 @@ bool BuildQuorumRotationInfo(CDeterministicMNManager& dmnman, CQuorumSnapshotMan 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); @@ -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); @@ -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); @@ -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); @@ -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); @@ -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; @@ -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 baseBlockIndexes, const CBlockIndex* blockIndex) +uint256 GetLastBaseBlockHash(Span 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; diff --git a/src/llmq/snapshot.h b/src/llmq/snapshot.h index db6f5ec307f7..071d3cbc69b3 100644 --- a/src/llmq/snapshot.h +++ b/src/llmq/snapshot.h @@ -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 baseBlockIndexes, const CBlockIndex* blockIndex); + bool use_legacy_construction, CQuorumRotationInfo& response, std::string& errorRet) + EXCLUSIVE_LOCKS_REQUIRED(::cs_main); +uint256 GetLastBaseBlockHash(Span baseBlockIndexes, const CBlockIndex* blockIndex, + bool use_legacy_construction); class CQuorumSnapshotManager { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7e0c768b33c5..5af1591298ee 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -5200,7 +5200,8 @@ void PeerManagerImpl::ProcessMessage( llmq::CQuorumRotationInfo quorumRotationInfoRet; std::string strError; - if (BuildQuorumRotationInfo(*m_dmnman, *m_llmq_ctx->qsnapman, m_chainman, *m_llmq_ctx->qman, *m_llmq_ctx->quorum_block_processor, cmd, quorumRotationInfoRet, strError)) { + bool use_legacy_construction = pfrom.GetCommonVersion() < EFFICIENT_QRINFO_VERSION;; + if (BuildQuorumRotationInfo(*m_dmnman, *m_llmq_ctx->qsnapman, m_chainman, *m_llmq_ctx->qman, *m_llmq_ctx->quorum_block_processor, cmd, use_legacy_construction, quorumRotationInfoRet, strError)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QUORUMROTATIONINFO, quorumRotationInfoRet)); } else { strError = strprintf("getquorumrotationinfo failed for size(baseBlockHashes)=%d, blockRequestHash=%s. error=%s", cmd.baseBlockHashes.size(), cmd.blockRequestHash.ToString(), strError); diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index 4baffb798553..1774621fbd4d 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -864,7 +864,7 @@ static RPCHelpMan quorum_rotationinfo() LOCK(cs_main); if (!BuildQuorumRotationInfo(*CHECK_NONFATAL(node.dmnman), *llmq_ctx.qsnapman, chainman, *llmq_ctx.qman, - *llmq_ctx.quorum_block_processor, cmd, quorumRotationInfoRet, strError)) { + *llmq_ctx.quorum_block_processor, cmd, false, quorumRotationInfoRet, strError)) { throw JSONRPCError(RPC_INVALID_REQUEST, strError); } diff --git a/src/test/bls_tests.cpp b/src/test/bls_tests.cpp index aef7f47f9f6e..008b245953c8 100644 --- a/src/test/bls_tests.cpp +++ b/src/test/bls_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -472,4 +473,124 @@ BOOST_AUTO_TEST_CASE(bls_threshold_signature_tests) FuncThresholdSignature(false); } +// A dummy BLS object that satisfies the minimal interface expected by CBLSLazyWrapper. +class DummyBLS +{ +public: + // Define a fixed serialization size (for testing purposes). + static const size_t SerSize = 4; + std::array data{}; + + DummyBLS() { data.fill(0); } + + // A dummy validity check: valid if any byte is non-zero. + bool IsValid() const + { + return std::any_of(data.begin(), data.end(), [](uint8_t c) { return c != 0; }); + } + + // Convert to bytes; ignore the legacy flag for simplicity. + std::array ToBytes(bool /*legacy*/) const { return data; } + + // Set from bytes; again, ignore the legacy flag. + void SetBytes(const std::array& bytes, bool /*legacy*/) { data = bytes; } + + // A dummy malleability check: simply compares the stored data to the given bytes. + bool CheckMalleable(const std::array& bytes, bool /*legacy*/) const { return data == bytes; } + + // Reset the object to an "empty" state. + void Reset() { data.fill(0); } + + // Produce a string representation. + std::string ToString(bool /*legacy*/) const { return HexStr(data); } + + // Equality operator. + bool operator==(const DummyBLS& other) const { return data == other.data; } +}; + +// Define a type alias for our lazy wrapper instantiated with DummyBLS. +using LazyDummyBLS = CBLSLazyWrapper; + +// Test 1: Two default (unset) wrappers should compare equal. +BOOST_AUTO_TEST_CASE(test_default_equality) +{ + LazyDummyBLS lazy1; + LazyDummyBLS lazy2; + // Neither instance has been set, so they represent the default/null object. + BOOST_CHECK(lazy1 == lazy2); +} + +// Test 2: A default wrapper and one initialized with a nonzero DummyBLS should compare unequal. +BOOST_AUTO_TEST_CASE(test_non_default_vs_default) +{ + LazyDummyBLS lazy_default; + LazyDummyBLS lazy_set; + DummyBLS obj; + obj.data = {1, 0, 0, 0}; // nonzero data makes the object valid + lazy_set.Set(obj, false); + BOOST_CHECK(!(lazy_default == lazy_set)); + BOOST_CHECK(lazy_default != lazy_set); +} + +// Test 2: A default wrapper and one initialized with a nonzero DummyBLS should compare unequal. +BOOST_AUTO_TEST_CASE(test_non_default_vs_different) +{ + LazyDummyBLS lazy_a; + LazyDummyBLS lazy_b; + DummyBLS obj; + obj.data = {1, 2, 3, 4}; // nonzero data makes the object valid + lazy_a.Set(obj, false); + obj.data = {2, 2, 3, 4}; // nonzero data makes the object valid + lazy_b.Set(obj, false); + BOOST_CHECK(lazy_a != lazy_b); +} + +// Test 3: Two wrappers set with the same underlying DummyBLS value compare equal. +BOOST_AUTO_TEST_CASE(test_equality_same_value) +{ + LazyDummyBLS lazy1; + LazyDummyBLS lazy2; + BOOST_CHECK(lazy1 == lazy2); + DummyBLS obj; + obj.data = {5, 6, 7, 8}; + lazy1.Set(obj, false); + BOOST_CHECK(lazy1 != lazy2); + lazy2.Set(obj, false); + BOOST_CHECK(lazy1 == lazy2); +} + +// Test 4: Serialization and unserialization preserve the wrapped value. +BOOST_AUTO_TEST_CASE(test_serialization_unserialization) +{ + LazyDummyBLS lazy1; + DummyBLS obj; + obj.data = {9, 10, 11, 12}; + // Set with a specific legacy flag (true in this case) + lazy1.Set(obj, true); + + // Serialize the lazy object into a data stream. + CDataStream ds(SER_DISK, CLIENT_VERSION); + lazy1.Serialize(ds, true); + + // Create a new instance and unserialize the data into it. + LazyDummyBLS lazy2; + lazy2.Unserialize(ds, true); + BOOST_CHECK(lazy1 == lazy2); + BOOST_CHECK(lazy2.Get() == obj); +} + +// Test 5: Two wrappers wrapping the same object should have the same hash. +BOOST_AUTO_TEST_CASE(test_get_hash_consistency) +{ + LazyDummyBLS lazy1; + LazyDummyBLS lazy2; + DummyBLS obj; + obj.data = {13, 14, 15, 16}; + lazy1.Set(obj, false); + lazy2.Set(obj, false); + uint256 hash1 = lazy1.GetHash(); + uint256 hash2 = lazy2.GetHash(); + BOOST_CHECK(hash1 == hash2); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/version.h b/src/version.h index 6be9c32090ad..36c096eb0c53 100644 --- a/src/version.h +++ b/src/version.h @@ -11,7 +11,7 @@ */ -static const int PROTOCOL_VERSION = 70235; +static const int PROTOCOL_VERSION = 70237; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -61,6 +61,12 @@ static const int DSQ_INV_VERSION = 70234; //! Maximum header count for HEADRES2 message was increased from 2000 to 8000 in this version static const int INCREASE_MAX_HEADERS2_VERSION = 70235; +//! Behavior of QRINFO is changed in this protocol version +static const int EFFICIENT_QRINFO_VERSION = 70236; + +//! cycleHash in isdlock message switched to using quorum's base block in this version +static const int ISDLOCK_CYCLEHASH_UPDATE_VERSION = 70237; + // Make sure that none of the values above collide with `ADDRV2_FORMAT`. #endif // BITCOIN_VERSION_H diff --git a/src/wallet/test/coinjoin_tests.cpp b/src/wallet/test/coinjoin_tests.cpp index 9335ae0098e9..768d5197675f 100644 --- a/src/wallet/test/coinjoin_tests.cpp +++ b/src/wallet/test/coinjoin_tests.cpp @@ -139,11 +139,13 @@ class CTransactionBuilderTestSetup : public TestChain100Setup LOCK2(wallet->cs_wallet, cs_main); wallet->GetLegacyScriptPubKeyMan()->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); wallet->SetLastBlockProcessed(m_node.chainman->ActiveChain().Height(), m_node.chainman->ActiveChain().Tip()->GetBlockHash()); - WalletRescanReserver reserver(*wallet); - reserver.reserve(); - CWallet::ScanResult result = wallet->ScanForWalletTransactions(m_node.chainman->ActiveChain().Genesis()->GetBlockHash(), 0 /* start_height */, {} /* max_height */, reserver, true /* fUpdate */); - BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); } + WalletRescanReserver reserver(*wallet); + reserver.reserve(); + CWallet::ScanResult result = wallet->ScanForWalletTransactions(/*start_block=*/wallet->chain().getBlockHash(0), + /*start_height=*/0, /*max_height=*/{}, reserver, + /*fUpdate=*/true); + BOOST_CHECK_EQUAL(result.status, CWallet::ScanResult::SUCCESS); } ~CTransactionBuilderTestSetup() diff --git a/test/functional/feature_llmq_rotation.py b/test/functional/feature_llmq_rotation.py index e1283e27ee41..cd52df2a6df3 100755 --- a/test/functional/feature_llmq_rotation.py +++ b/test/functional/feature_llmq_rotation.py @@ -231,6 +231,22 @@ def run_test(self): self.wait_until(lambda: self.nodes[0].getbestblockhash() == new_quorum_blockhash) assert_equal(self.nodes[0].quorum("list", llmq_type), new_quorum_list) + self.log.info("Test 'quorum rotationinfo' RPC") + genesis_blockhash = self.nodes[0].getblockhash(0) + block_count = self.nodes[0].getblockcount() + hmc_base_blockhash = self.nodes[0].getblockhash(block_count - (block_count % 24) - 24 - 8) + best_block_hash = self.nodes[0].getbestblockhash() + rpc_qr_info = self.nodes[0].quorum("rotationinfo", best_block_hash, False, hmc_base_blockhash) + assert_equal(rpc_qr_info["mnListDiffTip"]["blockHash"], best_block_hash) + assert_equal(rpc_qr_info["mnListDiffTip"]["baseBlockHash"], rpc_qr_info["mnListDiffH"]["blockHash"]) + assert_equal(rpc_qr_info["mnListDiffH"]["baseBlockHash"], rpc_qr_info["mnListDiffAtHMinusC"]["blockHash"]) + assert_equal(rpc_qr_info["mnListDiffAtHMinusC"]["blockHash"], hmc_base_blockhash) + assert_equal(rpc_qr_info["mnListDiffAtHMinusC"]["baseBlockHash"], hmc_base_blockhash) + assert_equal(rpc_qr_info["mnListDiffAtHMinusC"]["newQuorums"], []) + assert_equal(rpc_qr_info["mnListDiffAtHMinusC"]["deletedQuorums"], []) + assert_equal(rpc_qr_info["mnListDiffAtHMinus2C"]["baseBlockHash"], rpc_qr_info["mnListDiffAtHMinus3C"]["blockHash"]) + assert_equal(rpc_qr_info["mnListDiffAtHMinus3C"]["baseBlockHash"], genesis_blockhash) + def test_getmnlistdiff_quorums(self, baseBlockHash, blockHash, baseQuorumList, expectedDeleted, expectedNew, testQuorumsCLSigs = True): d = self.test_getmnlistdiff_base(baseBlockHash, blockHash, testQuorumsCLSigs) diff --git a/test/functional/test_framework/p2p.py b/test/functional/test_framework/p2p.py index f3da60e11320..1eed6df9a933 100755 --- a/test/functional/test_framework/p2p.py +++ b/test/functional/test_framework/p2p.py @@ -99,8 +99,8 @@ # The minimum P2P version that this test framework supports MIN_P2P_VERSION_SUPPORTED = 60001 # The P2P version that this test framework implements and sends in its `version` message -# Version 70235 increased max header count for HEADERS2 message from 2000 to 8000 -P2P_VERSION = 70235 +# Version 70237 switched cycleHash in isdlock message to using quorum's base block +P2P_VERSION = 70237 # The services that this test framework offers in its `version` message P2P_SERVICES = NODE_NETWORK | NODE_HEADERS_COMPRESSED # The P2P user agent string that this test framework sends in its `version` message