diff --git a/configure.ac b/configure.ac index ea0d3acf00..74de8a96b0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ AC_PREREQ([2.69]) define(_CLIENT_VERSION_MAJOR, 18) define(_CLIENT_VERSION_MINOR, 2) -define(_CLIENT_VERSION_BUILD, 1) +define(_CLIENT_VERSION_BUILD, 2) define(_CLIENT_VERSION_RC, 0) define(_CLIENT_VERSION_IS_RELEASE, true) define(_COPYRIGHT_YEAR, 2023) diff --git a/doc/release-notes.md b/doc/release-notes.md index 53e4e78995..51c7891cb6 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,17 +1,15 @@ -Dash Core version v18.2.1 +Dash Core version v18.2.2 ========================= Release is now available from: -This is a new hotfix version release, bringing various bugfixes. +This is a new hotfix version release. -Please note that v18.2.0 was revoked due to a bug; this version fixes that bug. - -This release is optional for all nodes; however, v18.2.1 is required to be -able to use both mainnet and testnet. Currently, v18.2.0 is not working on mainnet, -and v18.1.1 is not working on testnet; v18.2.1 will work on both networks. +This release is optional for all nodes; however, v18.2.2 or higher is required +to be able to use testnet right until v19 hard fork activation. Earlier +versions will not be able to sync past block 847000 on testnet. Please report bugs using the issue tracker at GitHub: @@ -41,9 +39,9 @@ downgrade to an older version is only possible with a reindex Downgrade warning ----------------- -### Downgrade to a version < v18.2.1 +### Downgrade to a version < v18.2.2 -Downgrading to a version older than v18.2.1 is supported. +Downgrading to a version older than v18.2.2 is supported. ### Downgrade to a version < v18.0.1 @@ -51,35 +49,19 @@ Downgrading to a version older than v18.0.1 is not supported due to changes in the indexes database folder. If you need to use an older version, you must either reindex or re-sync the whole chain. -### Downgrade of masternodes to < 18.2.1 - -It is highly recommended not to downgrade masternodes below 18.2.1, as 18.2.1 (and 18.1.1) -fix important bugs which may result in your masternode being PoSe banned. - -### Downgrade of masternodes to < v18.0.1 - -Starting with the 0.16 release, masternodes verify the protocol version of other -masternodes. This results in PoSe punishment/banning for outdated masternodes, -so downgrading even prior to the activation of the introduced hard-fork changes -is not recommended. - -Versioning ----------- - -Dash Core imperfectly follows semantic versioning. Breaking changes should be -expected in a major release. The number and severity of breaking changes in minor -releases are minimized, however we do not guarantee there are no breaking changes. -Bitcoin backports often introduce breaking changes, and are a likely source of -breaking changes in minor releases. Patch releases should never contain breaking changes. - Notable changes =============== -See #5145 and #5142; these 2 PR fix important bugs in previous versions. Specifically, -#5145 fixes an issue where qfcommit messages can be replayed from the past, then are -validated and propagated to other nodes. This patch prevents old qfcommits -from being relayed. #5142 is a fix which enables this version to function both on testnet -and mainnet. +Testnet Breaking Changes +------------------------ + +A new testnet only LLMQ has been added. This LLMQ is of the type LLMQ_25_67; this LLMQ is only active on testnet. +This LLMQ will not remove the LLMQ_100_67 from testnet; however that quorum (likely) will not form and will perform no role. +See the [diff](https://github.com/dashpay/dash/pull/5225/files#diff-e70a38a3e8c2a63ca0494627301a5c7042141ad301193f78338d97cb1b300ff9R451-R469) for specific parameters of the LLMQ. + +This LLMQ will become active at the height of 847000. **This will be a breaking change and a hard fork for testnet** +This LLMQ is not activated with the v19 hardfork; as such testnet will experience two hardforks. One at height 847000, +and the other to be determined by the BIP9 hard fork process. Remote Procedure Call (RPC) Changes ----------------------------------- @@ -109,20 +91,21 @@ Backports from Bitcoin Core --------------------------- None -v18.2.1 Change log +Other changes +------------- +#5247 is backported to improve debugging experience. + +v18.2.2 Change log ================== -See detailed [set of changes](https://github.com/dashpay/dash/compare/v18.2.0...dashpay:v18.2.1). +See detailed [set of changes](https://github.com/dashpay/dash/compare/v18.2.1...dashpay:v18.2.2). Credits ======= Thanks to everyone who directly contributed to this release: -- Kittywhiskers Van Gogh -- Konstantin Akimov - Odysseas Gabrielides -- PastaPastaPasta - UdjinM6 As well as everyone that submitted issues, reviewed pull requests, helped debug the release candidates, and write DIPs that were implemented in this release. @@ -150,6 +133,7 @@ Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. These release are considered obsolete. Old release notes can be found here: +- [v18.2.1](https://github.com/dashpay/dash/blob/master/doc/release-notes/dash/release-notes-18.2.2.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 diff --git a/doc/release-notes/dash/release-notes-18.2.1.md b/doc/release-notes/dash/release-notes-18.2.1.md new file mode 100644 index 0000000000..53e4e78995 --- /dev/null +++ b/doc/release-notes/dash/release-notes-18.2.1.md @@ -0,0 +1,187 @@ +Dash Core version v18.2.1 +========================= + +Release is now available from: + + + +This is a new hotfix version release, bringing various bugfixes. + +Please note that v18.2.0 was revoked due to a bug; this version fixes that bug. + +This release is optional for all nodes; however, v18.2.1 is required to be +able to use both mainnet and testnet. Currently, v18.2.0 is not working on mainnet, +and v18.1.1 is not working on testnet; v18.2.1 will work on both networks. + +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). If you upgrade after DIP0003 activation and you were +using version < 0.13 you will have to reindex (start with -reindex-chainstate +or -reindex) to make sure your wallet has all the new data synced. Upgrading +from version 0.13 should not require any additional actions. + +When upgrading from a version prior to 18.0.1, the +first startup of Dash Core will run a migration process which can take anywhere +from a few minutes to thirty minutes to finish. After the migration, a +downgrade to an older version is only possible with a reindex +(or reindex-chainstate). + +Downgrade warning +----------------- + +### Downgrade to a version < v18.2.1 + +Downgrading to a version older than v18.2.1 is supported. + +### Downgrade to a version < v18.0.1 + +Downgrading to a version older than v18.0.1 is not supported due to changes in +the indexes database folder. If you need to use an older version, you must +either reindex or re-sync the whole chain. + +### Downgrade of masternodes to < 18.2.1 + +It is highly recommended not to downgrade masternodes below 18.2.1, as 18.2.1 (and 18.1.1) +fix important bugs which may result in your masternode being PoSe banned. + +### Downgrade of masternodes to < v18.0.1 + +Starting with the 0.16 release, masternodes verify the protocol version of other +masternodes. This results in PoSe punishment/banning for outdated masternodes, +so downgrading even prior to the activation of the introduced hard-fork changes +is not recommended. + +Versioning +---------- + +Dash Core imperfectly follows semantic versioning. Breaking changes should be +expected in a major release. The number and severity of breaking changes in minor +releases are minimized, however we do not guarantee there are no breaking changes. +Bitcoin backports often introduce breaking changes, and are a likely source of +breaking changes in minor releases. Patch releases should never contain breaking changes. + +Notable changes +=============== +See #5145 and #5142; these 2 PR fix important bugs in previous versions. Specifically, +#5145 fixes an issue where qfcommit messages can be replayed from the past, then are +validated and propagated to other nodes. This patch prevents old qfcommits +from being relayed. #5142 is a fix which enables this version to function both on testnet +and mainnet. + + +Remote Procedure Call (RPC) Changes +----------------------------------- + +### The new RPCs are: +None + +### The removed RPCs are: +None + +### Changes in existing RPCs introduced through bitcoin backports: +None + +### Dash-specific changes in existing RPCs: +None + +Please check `help ` for more detailed information on specific RPCs. + +Command-line options +-------------------- +None + +Please check `Help -> Command-line options` in Qt wallet or `dashd --help` for +more information. + +Backports from Bitcoin Core +--------------------------- +None + +v18.2.1 Change log +================== + +See detailed [set of changes](https://github.com/dashpay/dash/compare/v18.2.0...dashpay:v18.2.1). + +Credits +======= + +Thanks to everyone who directly contributed to this release: + +- Kittywhiskers Van Gogh +- Konstantin Akimov +- Odysseas Gabrielides +- PastaPastaPasta +- UdjinM6 + +As well as everyone that submitted issues, reviewed pull requests, helped debug the release candidates, and write DIPs that were implemented in this release. + +Older releases +============== + +Dash was previously known as Darkcoin. + +Darkcoin tree 0.8.x was a fork of Litecoin tree 0.8, original name was XCoin +which was first released on Jan/18/2014. + +Darkcoin tree 0.9.x was the open source implementation of masternodes based on +the 0.8.x tree and was first released on Mar/13/2014. + +Darkcoin tree 0.10.x used to be the closed source implementation of Darksend +which was released open source on Sep/25/2014. + +Dash Core tree 0.11.x was a fork of Bitcoin Core tree 0.9, +Darkcoin was rebranded to Dash. + +Dash Core tree 0.12.0.x was a fork of Bitcoin Core tree 0.10. + +Dash Core tree 0.12.1.x was a fork of Bitcoin Core tree 0.12. + +These release are considered obsolete. Old release notes can be found here: + +- [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 diff --git a/src/chainparams.cpp b/src/chainparams.cpp index d1d86b12fc..a9f5395451 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -110,7 +110,7 @@ static CBlock FindDevNetGenesisBlock(const CBlock &prevBlock, const CAmount& rew void CChainParams::AddLLMQ(Consensus::LLMQType llmqType) { - assert(!HasLLMQ(llmqType)); + assert(!GetLLMQ(llmqType).has_value()); for (const auto& llmq_param : Consensus::available_llmqs) { if (llmq_param.type == llmqType) { consensus.llmqs.push_back(llmq_param); @@ -121,25 +121,14 @@ void CChainParams::AddLLMQ(Consensus::LLMQType llmqType) assert(false); } -const Consensus::LLMQParams& CChainParams::GetLLMQ(Consensus::LLMQType llmqType) const +std::optional CChainParams::GetLLMQ(Consensus::LLMQType llmqType) const { for (const auto& llmq_param : consensus.llmqs) { if (llmq_param.type == llmqType) { - return llmq_param; + return std::make_optional(llmq_param); } } - error("CChainParams::%s: unknown LLMQ type %d", __func__, static_cast(llmqType)); - assert(false); -} - -bool CChainParams::HasLLMQ(Consensus::LLMQType llmqType) const -{ - for (const auto& llmq_param : consensus.llmqs) { - if (llmq_param.type == llmqType) { - return true; - } - } - return false; + return std::nullopt; } /** @@ -523,10 +512,11 @@ public: AddLLMQ(Consensus::LLMQType::LLMQ_400_60); AddLLMQ(Consensus::LLMQType::LLMQ_400_85); AddLLMQ(Consensus::LLMQType::LLMQ_100_67); + AddLLMQ(Consensus::LLMQType::LLMQ_25_67); consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypeInstantSend = Consensus::LLMQType::LLMQ_50_60; consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_60_75; - consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_100_67; + consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_25_67; consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_50_60; fDefaultConsistencyChecks = false; @@ -1230,7 +1220,10 @@ void CDevNetParams::UpdateDevnetLLMQChainLocksFromArgs(const ArgsManager& args) { if (!args.IsArgSet("-llmqchainlocks")) return; - std::string strLLMQType = gArgs.GetArg("-llmqchainlocks", std::string(GetLLMQ(consensus.llmqTypeChainLocks).name)); + const auto& llmq_params_opt = GetLLMQ(consensus.llmqTypeChainLocks); + assert(llmq_params_opt.has_value()); + + std::string strLLMQType = gArgs.GetArg("-llmqchainlocks", std::string(llmq_params_opt->name)); Consensus::LLMQType llmqType = Consensus::LLMQType::LLMQ_NONE; for (const auto& params : consensus.llmqs) { @@ -1252,7 +1245,10 @@ void CDevNetParams::UpdateDevnetLLMQInstantSendFromArgs(const ArgsManager& args) { if (!args.IsArgSet("-llmqinstantsend")) return; - std::string strLLMQType = gArgs.GetArg("-llmqinstantsend", std::string(GetLLMQ(consensus.llmqTypeInstantSend).name)); + const auto& llmq_params_opt = GetLLMQ(consensus.llmqTypeInstantSend); + assert(llmq_params_opt.has_value()); + + std::string strLLMQType = gArgs.GetArg("-llmqinstantsend", std::string(llmq_params_opt->name)); Consensus::LLMQType llmqType = Consensus::LLMQType::LLMQ_NONE; for (const auto& params : consensus.llmqs) { @@ -1274,7 +1270,10 @@ void CDevNetParams::UpdateDevnetLLMQInstantSendDIP0024FromArgs(const ArgsManager { if (!args.IsArgSet("-llmqinstantsenddip0024")) return; - std::string strLLMQType = gArgs.GetArg("-llmqinstantsenddip0024", std::string(GetLLMQ(consensus.llmqTypeDIP0024InstantSend).name)); + const auto& llmq_params_opt = GetLLMQ(consensus.llmqTypeDIP0024InstantSend); + assert(llmq_params_opt.has_value()); + + std::string strLLMQType = gArgs.GetArg("-llmqinstantsenddip0024", std::string(llmq_params_opt->name)); Consensus::LLMQType llmqType = Consensus::LLMQType::LLMQ_NONE; for (const auto& params : consensus.llmqs) { diff --git a/src/chainparams.h b/src/chainparams.h index 18377207f6..317d7fc48a 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -110,8 +110,7 @@ public: const std::vector& SporkAddresses() const { return vSporkAddresses; } int MinSporkKeys() const { return nMinSporkKeys; } bool BIP9CheckMasternodesUpgraded() const { return fBIP9CheckMasternodesUpgraded; } - const Consensus::LLMQParams& GetLLMQ(Consensus::LLMQType llmqType) const; - bool HasLLMQ(Consensus::LLMQType llmqType) const; + std::optional GetLLMQ(Consensus::LLMQType llmqType) const; protected: CChainParams() {} diff --git a/src/evo/cbtx.cpp b/src/evo/cbtx.cpp index 76519f4b73..b44b4f4d90 100644 --- a/src/evo/cbtx.cpp +++ b/src/evo/cbtx.cpp @@ -188,7 +188,9 @@ auto CachedGetQcHashesQcIndexedHashes(const CBlockIndex* pindexPrev, const llmq: qcIndexedHashes_cached.clear(); for (const auto& [llmqType, vecBlockIndexes] : quorums) { - bool rotation_enabled = llmq::utils::IsQuorumRotationEnabled(llmqType, pindexPrev); + const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + bool rotation_enabled = llmq::utils::IsQuorumRotationEnabled(llmq_params_opt.value(), pindexPrev); auto& vec_hashes = qcHashes_cached[llmqType]; vec_hashes.reserve(vecBlockIndexes.size()); auto& map_indexed_hashes = qcIndexedHashes_cached[llmqType]; @@ -252,12 +254,16 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre // having null commitments is ok but we don't use them here, move to the next tx continue; } + const auto& llmq_params_opt = llmq::GetLLMQParams(qc.commitment.llmqType); + if (!llmq_params_opt.has_value()) { + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-commitment-type-calc-cbtx-quorummerkleroot"); + } + const auto& llmq_params = llmq_params_opt.value(); auto qcHash = ::SerializeHash(qc.commitment); - if (llmq::utils::IsQuorumRotationEnabled(qc.commitment.llmqType, pindexPrev)) { + if (llmq::utils::IsQuorumRotationEnabled(llmq_params, pindexPrev)) { auto& map_indexed_hashes = qcIndexedHashes[qc.commitment.llmqType]; map_indexed_hashes[qc.commitment.quorumIndex] = qcHash; } else { - const auto& llmq_params = llmq::GetLLMQParams(qc.commitment.llmqType); auto& vec_hashes = qcHashes[llmq_params.type]; if (vec_hashes.size() == size_t(llmq_params.signingActiveQuorumCount)) { // we pop the last entry, which is actually the oldest quorum as GetMinedAndActiveCommitmentsUntilBlock @@ -281,8 +287,9 @@ bool CalcCbTxMerkleRootQuorums(const CBlock& block, const CBlockIndex* pindexPre vec_hashes_final.reserve(CalcHashCountFromQCHashes(qcHashes)); for (const auto& [llmqType, vec_hashes] : qcHashes) { - const auto& llmq_params = llmq::GetLLMQParams(llmqType); - if (vec_hashes.size() > size_t(llmq_params.signingActiveQuorumCount)) { + const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + if (vec_hashes.size() > size_t(llmq_params_opt->signingActiveQuorumCount)) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "excess-quorums-calc-cbtx-quorummerkleroot"); } // Copy vec_hashes into vec_hashes_final diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 1be2c7c08d..0326c54080 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -829,9 +829,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload"); } if (!qc.commitment.IsNull()) { - const auto& llmq_params = llmq::GetLLMQParams(qc.commitment.llmqType); + const auto& llmq_params_opt = llmq::GetLLMQParams(qc.commitment.llmqType); + if (!llmq_params_opt.has_value()) { + return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-commitment-type"); + } int qcnHeight = int(qc.nHeight); - int quorumHeight = qcnHeight - (qcnHeight % llmq_params.dkgInterval) + int(qc.commitment.quorumIndex); + int quorumHeight = qcnHeight - (qcnHeight % llmq_params_opt->dkgInterval) + int(qc.commitment.quorumIndex); auto pQuorumBaseBlockIndex = pindexPrev->GetAncestor(quorumHeight); if (!pQuorumBaseBlockIndex || pQuorumBaseBlockIndex->GetBlockHash() != qc.commitment.quorumHash) { // we should actually never get into this case as validation should have caught it...but let's be sure diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index 68817c8eb4..b0815b1f2b 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -25,7 +25,9 @@ bool MNHFTx::Verify(const CBlockIndex* pQuorumIndex) const } Consensus::LLMQType llmqType = Params().GetConsensus().llmqTypeMnhf; - int signOffset{llmq::GetLLMQParams(llmqType).dkgInterval}; + const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + int signOffset{llmq_params_opt->dkgInterval}; const uint256 requestId = ::SerializeHash(std::make_pair(CBLSIG_REQUESTID_PREFIX, pQuorumIndex->nHeight)); return llmq::CSigningManager::VerifyRecoveredSig(llmqType, *llmq::quorumManager, pQuorumIndex->nHeight, requestId, pQuorumIndex->GetBlockHash(), sig, 0) || @@ -53,7 +55,7 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidat return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-quorum-hash"); } - if (!Params().HasLLMQ(Params().GetConsensus().llmqTypeMnhf)) { + if (!llmq::GetLLMQParams(Params().GetConsensus().llmqTypeMnhf).has_value()) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-type"); } diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index cc41da5623..83496f3bf3 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -58,7 +58,8 @@ void CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view m return; } - if (!Params().HasLLMQ(qc.llmqType)) { + const auto& llmq_params_opt = GetLLMQParams(qc.llmqType); + if (!llmq_params_opt.has_value()) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- invalid commitment type %d from peer=%d\n", __func__, uint8_t(qc.llmqType), peer.GetId()); WITH_LOCK(cs_main, Misbehaving(peer.GetId(), 100)); @@ -84,14 +85,14 @@ void CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view m // same, can't punish return; } - int quorumHeight = pQuorumBaseBlockIndex->nHeight - (pQuorumBaseBlockIndex->nHeight % GetLLMQParams(type).dkgInterval) + int(qc.quorumIndex); + int quorumHeight = pQuorumBaseBlockIndex->nHeight - (pQuorumBaseBlockIndex->nHeight % llmq_params_opt->dkgInterval) + int(qc.quorumIndex); if (quorumHeight != pQuorumBaseBlockIndex->nHeight) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s is not the first block in the DKG interval, peer=%d\n", __func__, qc.quorumHash.ToString(), peer.GetId()); Misbehaving(peer.GetId(), 100); return; } - if (pQuorumBaseBlockIndex->nHeight < (::ChainActive().Height() - GetLLMQParams(type).dkgInterval)) { + if (pQuorumBaseBlockIndex->nHeight < (::ChainActive().Height() - llmq_params_opt->dkgInterval)) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- block %s is too old, peer=%d\n", __func__, qc.quorumHash.ToString(), peer.GetId()); // TODO: enable punishment in some future version when all/most nodes are running with this fix @@ -173,7 +174,7 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* if (numCommitmentsRequired > numCommitmentsInNewBlock) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-missing"); } - if (llmq::utils::IsQuorumRotationEnabled(params.type, pindex)) { + if (llmq::utils::IsQuorumRotationEnabled(params, pindex)) { LogPrintf("[ProcessBlock] h[%d] numCommitmentsRequired[%d] numCommitmentsInNewBlock[%d]\n", pindex->nHeight, numCommitmentsRequired, numCommitmentsInNewBlock); } } @@ -209,7 +210,12 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH { AssertLockHeld(cs_main); - const auto& llmq_params = GetLLMQParams(qc.llmqType); + const auto& llmq_params_opt = GetLLMQParams(qc.llmqType); + if (!llmq_params_opt.has_value()) { + LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- invalid commitment type %d\n", __func__, static_cast(qc.llmqType)); + return false; + } + const auto& llmq_params = llmq_params_opt.value(); uint256 quorumHash = GetQuorumBlockHash(llmq_params, nHeight, qc.quorumIndex); @@ -263,7 +269,7 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH return true; } - bool rotation_enabled = utils::IsQuorumRotationEnabled(llmq_params.type, pQuorumBaseBlockIndex); + bool rotation_enabled = utils::IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex); if (rotation_enabled) { LogPrint(BCLog::LLMQ, "[ProcessCommitment] height[%d] pQuorumBaseBlockIndex[%d] quorumIndex[%d] qversion[%d] Built\n", @@ -313,7 +319,10 @@ bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pi evoDb.Erase(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash))); - if (llmq::utils::IsQuorumRotationEnabled(qc.llmqType, pindex)) { + const auto& llmq_params_opt = GetLLMQParams(qc.llmqType); + assert(llmq_params_opt.has_value()); + + if (llmq::utils::IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) { evoDb.Erase(BuildInversedHeightKeyIndexed(qc.llmqType, pindex->nHeight, int(qc.quorumIndex))); } else { evoDb.Erase(BuildInversedHeightKey(qc.llmqType, pindex->nHeight)); @@ -372,7 +381,9 @@ bool CQuorumBlockProcessor::UpgradeDB() } const auto* pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash); evoDb.GetRawDB().Write(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)), std::make_pair(qc, pindex->GetBlockHash())); - if (llmq::utils::IsQuorumRotationEnabled(qc.llmqType, pQuorumBaseBlockIndex)) { + const auto& llmq_params_opt = GetLLMQParams(qc.llmqType); + assert(llmq_params_opt.has_value()); + if (llmq::utils::IsQuorumRotationEnabled(llmq_params_opt.value(), pQuorumBaseBlockIndex)) { evoDb.GetRawDB().Write(BuildInversedHeightKeyIndexed(qc.llmqType, pindex->nHeight, int(qc.quorumIndex)), pQuorumBaseBlockIndex->nHeight); } else { evoDb.GetRawDB().Write(BuildInversedHeightKey(qc.llmqType, pindex->nHeight), pQuorumBaseBlockIndex->nHeight); @@ -406,8 +417,14 @@ bool CQuorumBlockProcessor::GetCommitmentsFromBlock(const CBlock& block, const C return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload"); } + const auto& llmq_params_opt = GetLLMQParams(qc.commitment.llmqType); + if (!llmq_params_opt.has_value()) { + // should not happen as it was verified before processing the block + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-commitment-type"); + } + // only allow one commitment per type and per block (This was changed with rotation) - if (!utils::IsQuorumRotationEnabled(qc.commitment.llmqType, pindex)) { + if (!utils::IsQuorumRotationEnabled(llmq_params_opt.value(), pindex)) { if (ret.count(qc.commitment.llmqType) != 0) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-dup"); } @@ -454,7 +471,7 @@ size_t CQuorumBlockProcessor::GetNumCommitmentsRequired(const Consensus::LLMQPar assert(nHeight <= ::ChainActive().Height() + 1); const auto *const pindex = ::ChainActive().Height() < nHeight ? ::ChainActive().Tip() : ::ChainActive().Tip()->GetAncestor(nHeight); - bool rotation_enabled = utils::IsQuorumRotationEnabled(llmqParams.type, pindex); + bool rotation_enabled = utils::IsQuorumRotationEnabled(llmqParams, pindex); size_t quorums_num = rotation_enabled ? llmqParams.signingActiveQuorumCount : 1; size_t ret{0}; @@ -607,10 +624,11 @@ std::optional CQuorumBlockProcessor::GetLastMinedCommitments std::vector> CQuorumBlockProcessor::GetLastMinedCommitmentsPerQuorumIndexUntilBlock(Consensus::LLMQType llmqType, const CBlockIndex* pindex, size_t cycle) const { - const Consensus::LLMQParams& llmqParams = GetLLMQParams(llmqType); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); std::vector> ret; - for (const auto quorumIndex : irange::range(llmqParams.signingActiveQuorumCount)) { + for (const auto quorumIndex : irange::range(llmq_params_opt->signingActiveQuorumCount)) { std::optional q = GetLastMinedCommitmentsByQuorumIndexUntilBlock(llmqType, pindex, quorumIndex, cycle); if (q.has_value()) { ret.emplace_back(quorumIndex, q.value()); @@ -658,7 +676,7 @@ std::map> CQuorumBlockProce for (const auto& params : Params().GetConsensus().llmqs) { auto& v = ret[params.type]; v.reserve(params.signingActiveQuorumCount); - if (utils::IsQuorumRotationEnabled(params.type, pindex)) { + if (utils::IsQuorumRotationEnabled(params, pindex)) { std::vector> commitments = GetLastMinedCommitmentsPerQuorumIndexUntilBlock(params.type, pindex, 0); std::transform(commitments.begin(), commitments.end(), std::back_inserter(v), [](const std::pair& p) { return p.second; }); @@ -737,7 +755,7 @@ std::optional> CQuorumBlockProcessor::GetMineableC assert(nHeight <= ::ChainActive().Height() + 1); const auto *const pindex = ::ChainActive().Height() < nHeight ? ::ChainActive().Tip() : ::ChainActive().Tip()->GetAncestor(nHeight); - bool rotation_enabled = utils::IsQuorumRotationEnabled(llmqParams.type, pindex); + bool rotation_enabled = utils::IsQuorumRotationEnabled(llmqParams, pindex); size_t quorums_num = rotation_enabled ? llmqParams.signingActiveQuorumCount : 1; std::stringstream ss; diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp index 8af2373041..7677000e5f 100644 --- a/src/llmq/commitment.cpp +++ b/src/llmq/commitment.cpp @@ -30,16 +30,17 @@ CFinalCommitment::CFinalCommitment(const Consensus::LLMQParams& params, const ui bool CFinalCommitment::Verify(const CBlockIndex* pQuorumBaseBlockIndex, bool checkSigs) const { - if (nVersion == 0 || nVersion != (utils::IsQuorumRotationEnabled(llmqType, pQuorumBaseBlockIndex) ? INDEXED_QUORUM_VERSION : CURRENT_VERSION)) { - LogPrintfFinalCommitment("q[%s] invalid nVersion=%d\n", quorumHash.ToString(), nVersion); - return false; - } - - if (!Params().HasLLMQ(llmqType)) { + const auto& llmq_params_opt = GetLLMQParams(llmqType); + if (!llmq_params_opt.has_value()) { LogPrintfFinalCommitment("q[%s] invalid llmqType=%d\n", quorumHash.ToString(), static_cast(llmqType)); return false; } - const auto& llmq_params = GetLLMQParams(llmqType); + const auto& llmq_params = llmq_params_opt.value(); + + if (nVersion == 0 || nVersion != (utils::IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex) ? INDEXED_QUORUM_VERSION : CURRENT_VERSION)) { + LogPrintfFinalCommitment("q[%s] invalid nVersion=%d\n", quorumHash.ToString(), nVersion); + return false; + } if (pQuorumBaseBlockIndex->GetBlockHash() != quorumHash) { LogPrintfFinalCommitment("q[%s] invalid quorumHash\n", quorumHash.ToString()); @@ -138,12 +139,13 @@ bool CFinalCommitment::Verify(const CBlockIndex* pQuorumBaseBlockIndex, bool che bool CFinalCommitment::VerifyNull() const { - if (!Params().HasLLMQ(llmqType)) { + const auto& llmq_params_opt = GetLLMQParams(llmqType); + if (!llmq_params_opt.has_value()) { LogPrintfFinalCommitment("q[%s]invalid llmqType=%d\n", quorumHash.ToString(), static_cast(llmqType)); return false; } - if (!IsNull() || !VerifySizes(GetLLMQParams(llmqType))) { + if (!IsNull() || !VerifySizes(llmq_params_opt.value())) { return false; } @@ -170,10 +172,16 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, LogPrintfFinalCommitment("h[%d] GetTxPayload failed\n", pindexPrev->nHeight); return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-payload"); } - const auto& llmq_params = GetLLMQParams(qcTx.commitment.llmqType); + + const auto& llmq_params_opt = GetLLMQParams(qcTx.commitment.llmqType); + if (!llmq_params_opt.has_value()) { + LogPrintfFinalCommitment("h[%d] GetLLMQParams failed for llmqType[%d]\n", pindexPrev->nHeight, static_cast(qcTx.commitment.llmqType)); + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-commitment-type"); + } + if (LogAcceptCategory(BCLog::LLMQ)) { std::stringstream ss; - for (const auto i: irange::range(llmq_params.size)) { + for (const auto i: irange::range(llmq_params_opt->size)) { ss << "v[" << i << "]=" << qcTx.commitment.validMembers[i]; } LogPrintfFinalCommitment("%s llmqType[%d] validMembers[%s] signers[]\n", __func__, @@ -201,10 +209,6 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-quorum-hash"); } - if (!Params().HasLLMQ(qcTx.commitment.llmqType)) { - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-type"); - } - if (qcTx.commitment.IsNull()) { if (!qcTx.commitment.VerifyNull()) { LogPrintfFinalCommitment("h[%d] invalid qcTx.commitment[%s] VerifyNull failed\n", pindexPrev->nHeight, qcTx.commitment.quorumHash.ToString()); diff --git a/src/llmq/debug.cpp b/src/llmq/debug.cpp index a0f9ad444f..d3ad1cde9c 100644 --- a/src/llmq/debug.cpp +++ b/src/llmq/debug.cpp @@ -18,7 +18,7 @@ UniValue CDKGDebugSessionStatus::ToJson(int quorumIndex, int detailLevel) const { UniValue ret(UniValue::VOBJ); - if (!Params().HasLLMQ(llmqType) || quorumHash.IsNull()) { + if (!GetLLMQParams(llmqType).has_value() || quorumHash.IsNull()) { return ret; } @@ -117,11 +117,12 @@ UniValue CDKGDebugStatus::ToJson(int detailLevel) const // TODO Support array of sessions UniValue sessionsArrJson(UniValue::VARR); for (const auto& p : sessions) { - if (!Params().HasLLMQ(p.first.first)) { + const auto& llmq_params_opt = GetLLMQParams(p.first.first); + if (!llmq_params_opt.has_value()) { continue; } UniValue s(UniValue::VOBJ); - s.pushKV("llmqType", std::string(GetLLMQParams(p.first.first).name)); + s.pushKV("llmqType", std::string(llmq_params_opt->name)); s.pushKV("quorumIndex", p.first.second); s.pushKV("status", p.second.ToJson(p.first.second, detailLevel)); diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index b5ecafed47..53ba4d3859 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -85,7 +85,7 @@ bool CDKGSession::Init(const CBlockIndex* _pQuorumBaseBlockIndex, const std::vec CDKGLogger logger(*this, __func__); - if (LogAcceptCategory(BCLog::LLMQ) && utils::IsQuorumRotationEnabled(params.type, m_quorum_base_block_index)) { + if (LogAcceptCategory(BCLog::LLMQ) && utils::IsQuorumRotationEnabled(params, m_quorum_base_block_index)) { int cycleQuorumBaseHeight = m_quorum_base_block_index->nHeight - quorumIndex; const CBlockIndex* pCycleQuorumBaseBlockIndex = m_quorum_base_block_index->GetAncestor(cycleQuorumBaseHeight); std::stringstream ss; @@ -1206,7 +1206,7 @@ std::vector CDKGSession::FinalizeCommitments() fqc.quorumPublicKey = first.quorumPublicKey; fqc.quorumVvecHash = first.quorumVvecHash; - if (utils::IsQuorumRotationEnabled(fqc.llmqType, m_quorum_base_block_index)) { + if (utils::IsQuorumRotationEnabled(params, m_quorum_base_block_index)) { fqc.nVersion = CFinalCommitment::INDEXED_QUORUM_VERSION; fqc.quorumIndex = quorumIndex; } else { diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index 9c1e2a6a83..eaa6f159f6 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -256,7 +256,7 @@ class CDKGSession friend class CDKGLogger; private: - const Consensus::LLMQParams& params; + const Consensus::LLMQParams params; CBLSWorker& blsWorker; CBLSWorkerCache cache; diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index a81fb8635a..3ae45c93cb 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -84,7 +84,7 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) { //AssertLockNotHeld(cs_main); //Indexed quorums (greater than 0) are enabled with Quorum Rotation - if (quorumIndex > 0 && !utils::IsQuorumRotationEnabled(params.type, pindexNew)) { + if (quorumIndex > 0 && !utils::IsQuorumRotationEnabled(params, pindexNew)) { return; } LOCK(cs); diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index 3b7b337049..29b012bbb1 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -109,7 +109,7 @@ private: mutable CCriticalSection cs; std::atomic stopRequested{false}; - const Consensus::LLMQParams& params; + const Consensus::LLMQParams params; CConnman& connman; const int quorumIndex; CBLSWorker& blsWorker; diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 0852c8683d..4ed2a200b9 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -196,12 +196,14 @@ void CDKGSessionManager::ProcessMessage(CNode* pfrom, const CQuorumManager& quor vRecv.Rewind(sizeof(uint256)); vRecv.Rewind(sizeof(uint8_t)); - if (!Params().HasLLMQ(llmqType)) { + const auto& llmq_params_opt = GetLLMQParams(llmqType); + if (!llmq_params_opt.has_value()) { LOCK(cs_main); LogPrintf("CDKGSessionManager -- invalid llmqType [%d]\n", uint8_t(llmqType)); Misbehaving(pfrom->GetId(), 100); return; } + const auto& llmq_params = llmq_params_opt.value(); int quorumIndex{-1}; @@ -232,10 +234,9 @@ void CDKGSessionManager::ProcessMessage(CNode* pfrom, const CQuorumManager& quor return; } - const Consensus::LLMQParams& llmqParams = GetLLMQParams(llmqType); - quorumIndex = pQuorumBaseBlockIndex->nHeight % llmqParams.dkgInterval; - int quorumIndexMax = utils::IsQuorumRotationEnabled(llmqType, pQuorumBaseBlockIndex) ? - llmqParams.signingActiveQuorumCount - 1 : 0; + quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval; + int quorumIndexMax = utils::IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex) ? + llmq_params.signingActiveQuorumCount - 1 : 0; if (quorumIndex > quorumIndexMax) { LOCK(cs_main); diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp index 0581024b9a..423f98e1fb 100644 --- a/src/llmq/instantsend.cpp +++ b/src/llmq/instantsend.cpp @@ -706,8 +706,10 @@ void CInstantSendManager::TrySignInstantSendLock(const CTransaction& tx) // compute and set cycle hash if islock is deterministic if (islock.IsDeterministic()) { + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt); LOCK(cs_main); - const auto dkgInterval = GetLLMQParams(llmqType).dkgInterval; + const auto dkgInterval = llmq_params_opt->dkgInterval; const auto quorumHeight = ::ChainActive().Height() - (::ChainActive().Height() % dkgInterval); islock.cycleHash = ::ChainActive()[quorumHeight]->GetBlockHash(); } @@ -804,7 +806,9 @@ void CInstantSendManager::ProcessMessageInstantSendLock(const CNode* pfrom, cons // Deterministic islocks MUST use rotation based llmq auto llmqType = Params().GetConsensus().llmqTypeDIP0024InstantSend; - if (blockIndex->nHeight % GetLLMQParams(llmqType).dkgInterval != 0) { + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt); + if (blockIndex->nHeight % llmq_params_opt->dkgInterval != 0) { WITH_LOCK(cs_main, Misbehaving(pfrom->GetId(), 100)); return; } @@ -905,10 +909,13 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks(bool deterministic) //TODO Investigate if leaving this is ok auto llmqType = utils::GetInstantSendLLMQType(deterministic); - auto dkgInterval = GetLLMQParams(llmqType).dkgInterval; + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt); + const auto& llmq_params = llmq_params_opt.value(); + auto dkgInterval = llmq_params.dkgInterval; // First check against the current active set and don't ban - auto badISLocks = ProcessPendingInstantSendLocks(llmqType, 0, pend, false); + auto badISLocks = ProcessPendingInstantSendLocks(llmq_params, 0, pend, false); if (!badISLocks.empty()) { LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- doing verification on old active set\n", __func__); @@ -921,13 +928,13 @@ bool CInstantSendManager::ProcessPendingInstantSendLocks(bool deterministic) } } // Now check against the previous active set and perform banning if this fails - ProcessPendingInstantSendLocks(llmqType, dkgInterval, pend, true); + ProcessPendingInstantSendLocks(llmq_params, dkgInterval, pend, true); } return fMoreWork; } -std::unordered_set CInstantSendManager::ProcessPendingInstantSendLocks(const Consensus::LLMQType llmqType, int signOffset, const std::unordered_map, StaticSaltedHasher>& pend, bool ban) +std::unordered_set CInstantSendManager::ProcessPendingInstantSendLocks(const Consensus::LLMQParams& llmq_params, int signOffset, const std::unordered_map, StaticSaltedHasher>& pend, bool ban) { CBLSBatchVerifier batchVerifier(false, true, 8); std::unordered_map recSigs; @@ -951,7 +958,7 @@ std::unordered_set CInstantSendManager::ProcessPend auto id = islock->GetRequestId(); // no need to verify an ISLOCK if we already have verified the recovered sig that belongs to it - if (sigman.HasRecoveredSig(llmqType, id, islock->txid)) { + if (sigman.HasRecoveredSig(llmq_params.type, id, islock->txid)) { alreadyVerified++; continue; } @@ -966,26 +973,26 @@ std::unordered_set CInstantSendManager::ProcessPend continue; } - const auto dkgInterval = GetLLMQParams(llmqType).dkgInterval; + const auto dkgInterval = llmq_params.dkgInterval; if (blockIndex->nHeight + dkgInterval < ::ChainActive().Height()) { nSignHeight = blockIndex->nHeight + dkgInterval - 1; } } - auto quorum = llmq::CSigningManager::SelectQuorumForSigning(llmqType, qman, id, nSignHeight, signOffset); + auto quorum = llmq::CSigningManager::SelectQuorumForSigning(llmq_params, qman, id, nSignHeight, signOffset); if (!quorum) { // should not happen, but if one fails to select, all others will also fail to select return {}; } - uint256 signHash = utils::BuildSignHash(llmqType, quorum->qc->quorumHash, id, islock->txid); + uint256 signHash = utils::BuildSignHash(llmq_params.type, quorum->qc->quorumHash, id, islock->txid); batchVerifier.PushMessage(nodeId, hash, signHash, islock->sig.Get(), quorum->qc->quorumPublicKey); verifyCount++; // We can reconstruct the CRecoveredSig objects from the islock and pass it to the signing manager, which // avoids unnecessary double-verification of the signature. We however only do this when verification here // turns out to be good (which is checked further down) - if (!sigman.HasRecoveredSigForId(llmqType, id)) { - recSigs.try_emplace(hash, CRecoveredSig(llmqType, quorum->qc->quorumHash, id, islock->txid, islock->sig)); + if (!sigman.HasRecoveredSigForId(llmq_params.type, id)) { + recSigs.try_emplace(hash, CRecoveredSig(llmq_params.type, quorum->qc->quorumHash, id, islock->txid, islock->sig)); } } @@ -1025,7 +1032,7 @@ std::unordered_set CInstantSendManager::ProcessPend auto it = recSigs.find(hash); if (it != recSigs.end()) { auto recSig = std::make_shared(std::move(it->second)); - if (!sigman.HasRecoveredSigForId(llmqType, recSig->getId())) { + if (!sigman.HasRecoveredSigForId(llmq_params.type, recSig->getId())) { LogPrint(BCLog::INSTANTSEND, "CInstantSendManager::%s -- txid=%s, islock=%s: passing reconstructed recSig to signing mgr, peer=%d\n", __func__, islock->txid.ToString(), hash.ToString(), nodeId); sigman.PushReconstructedRecoveredSig(recSig); diff --git a/src/llmq/instantsend.h b/src/llmq/instantsend.h index 980bb96a46..a2e11fbaff 100644 --- a/src/llmq/instantsend.h +++ b/src/llmq/instantsend.h @@ -284,7 +284,7 @@ private: bool ProcessPendingInstantSendLocks(); bool ProcessPendingInstantSendLocks(bool deterministic) LOCKS_EXCLUDED(cs_pendingLocks); - std::unordered_set ProcessPendingInstantSendLocks(const Consensus::LLMQType llmqType, + std::unordered_set ProcessPendingInstantSendLocks(const Consensus::LLMQParams& llmq_params, int signOffset, const std::unordered_map, diff --git a/src/llmq/params.h b/src/llmq/params.h index 1a2d902907..d5c7e89347 100644 --- a/src/llmq/params.h +++ b/src/llmq/params.h @@ -19,6 +19,7 @@ enum class LLMQType : uint8_t { LLMQ_400_85 = 3, // 400 members, 340 (85%) threshold, one every 24 hours LLMQ_100_67 = 4, // 100 members, 67 (67%) threshold, one per hour LLMQ_60_75 = 5, // 60 members, 45 (75%) threshold, one every 12 hours + LLMQ_25_67 = 6, // 25 members, 67 (67%) threshold, one per hour // for testing only LLMQ_TEST = 100, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used @@ -105,8 +106,13 @@ struct LLMQParams { int recoveryMembers; }; +//static_assert(std::is_trivial_v, "LLMQParams is not a trivial type"); +static_assert(std::is_trivially_copyable_v, "LLMQParams is not trivially copyable"); +//static_assert(std::is_trivially_default_constructible_v, "LLMQParams is not trivially default constructible"); +static_assert(std::is_trivially_assignable_v, "LLMQParams is not trivially assignable"); -static constexpr std::array available_llmqs = { + +static constexpr std::array available_llmqs = { /** * llmq_test @@ -388,6 +394,33 @@ static constexpr std::array available_llmqs = { .recoveryMembers = 50, }, + /** + * llmq_25_67 + * This quorum is deployed on Testnet and requires + * 25 participants + * + * Used by Dash Platform + */ + LLMQParams{ + .type = LLMQType::LLMQ_25_67, + .name = "llmq_25_67", + .useRotation = false, + .size = 25, + .minSize = 22, + .threshold = 17, + + .dkgInterval = 24, // one DKG per hour + .dkgPhaseBlocks = 2, + .dkgMiningWindowStart = 10, // dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 18, + .dkgBadVotesThreshold = 22, + + .signingActiveQuorumCount = 24, // a full day worth of LLMQs + + .keepOldConnections = 25, + .recoveryMembers = 12, + }, + }; // available_llmqs } // namespace Consensus diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp index ef8d7203c2..e1927ee998 100644 --- a/src/llmq/quorums.cpp +++ b/src/llmq/quorums.cpp @@ -299,7 +299,7 @@ void CQuorumManager::CheckQuorumConnections(const Consensus::LLMQParams& llmqPar auto connmanQuorumsToDelete = connman.GetMasternodeQuorums(llmqParams.type); // don't remove connections for the currently in-progress DKG round - if (utils::IsQuorumRotationEnabled(llmqParams.type, pindexNew)) { + if (utils::IsQuorumRotationEnabled(llmqParams, pindexNew)) { int cycleIndexTipHeight = pindexNew->nHeight % llmqParams.dkgInterval; int cycleQuorumBaseHeight = pindexNew->nHeight - cycleIndexTipHeight; std::stringstream ss; @@ -371,7 +371,9 @@ CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType l } assert(qc->quorumHash == pQuorumBaseBlockIndex->GetBlockHash()); - auto quorum = std::make_shared(llmq::GetLLMQParams(llmqType), blsWorker); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + auto quorum = std::make_shared(llmq_params_opt.value(), blsWorker); auto members = utils::GetAllQuorumMembers(qc->llmqType, pQuorumBaseBlockIndex); quorum->Init(std::move(qc), pQuorumBaseBlockIndex, minedBlockHash, members); @@ -450,7 +452,7 @@ bool CQuorumManager::RequestQuorumData(CNode* pFrom, Consensus::LLMQType llmqTyp LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- pFrom is neither a verified masternode nor a qwatch connection\n", __func__); return false; } - if (!Params().HasLLMQ(llmqType)) { + if (!GetLLMQParams(llmqType).has_value()) { LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Invalid llmqType: %d\n", __func__, static_cast(llmqType)); return false; } @@ -523,7 +525,9 @@ std::vector CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp } // Get the block indexes of the mined commitments to build the required quorums from - std::vector pQuorumBaseBlockIndexes{ GetLLMQParams(llmqType).useRotation ? + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + std::vector pQuorumBaseBlockIndexes{ llmq_params_opt->useRotation ? quorumBlockProcessor.GetMinedCommitmentsIndexedUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) : quorumBlockProcessor.GetMinedCommitmentsUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) }; @@ -648,7 +652,7 @@ void CQuorumManager::ProcessMessage(CNode* pFrom, const std::string& msg_type, C } } - if (!Params().HasLLMQ(request.GetLLMQType())) { + if (!GetLLMQParams(request.GetLLMQType()).has_value()) { sendQDATA(CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID); return; } diff --git a/src/llmq/quorums.h b/src/llmq/quorums.h index 3073a80d4a..5e6ac37bb3 100644 --- a/src/llmq/quorums.h +++ b/src/llmq/quorums.h @@ -158,7 +158,7 @@ class CQuorum { friend class CQuorumManager; public: - const Consensus::LLMQParams& params; + const Consensus::LLMQParams params; CFinalCommitmentPtr qc; const CBlockIndex* m_quorum_base_block_index{nullptr}; uint256 minedBlockHash; diff --git a/src/llmq/signing.cpp b/src/llmq/signing.cpp index 8421a69644..c12f58c9c2 100644 --- a/src/llmq/signing.cpp +++ b/src/llmq/signing.cpp @@ -606,7 +606,7 @@ bool CSigningManager::PreVerifyRecoveredSig(const CQuorumManager& quorum_manager retBan = false; auto llmqType = recoveredSig.getLlmqType(); - if (!Params().HasLLMQ(llmqType)) { + if (!GetLLMQParams(llmqType).has_value()) { retBan = true; return false; } @@ -882,7 +882,9 @@ bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, CSigShares // This gives a slight risk of not getting enough shares to recover a signature // But at least it shouldn't be possible to get conflicting recovered signatures // TODO fix this by re-signing when the next block arrives, but only when that block results in a change of the quorum list and no recovered signature has been created in the mean time - quorum = SelectQuorumForSigning(llmqType, qman, id); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + quorum = SelectQuorumForSigning(llmq_params_opt.value(), qman, id); } else { quorum = qman.GetQuorum(llmqType, quorumHash); } @@ -979,9 +981,9 @@ bool CSigningManager::GetVoteForId(Consensus::LLMQType llmqType, const uint256& return db.GetVoteForId(llmqType, id, msgHashRet); } -CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType, const CQuorumManager& quorum_manager, const uint256& selectionHash, int signHeight, int signOffset) +CQuorumCPtr CSigningManager::SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CQuorumManager& quorum_manager, const uint256& selectionHash, int signHeight, int signOffset) { - size_t poolSize = GetLLMQParams(llmqType).signingActiveQuorumCount; + size_t poolSize = llmq_params.signingActiveQuorumCount; CBlockIndex* pindexStart; { @@ -996,13 +998,13 @@ CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType pindexStart = ::ChainActive()[startBlockHeight]; } - if (utils::IsQuorumRotationEnabled(llmqType, pindexStart)) { - auto quorums = quorum_manager.ScanQuorums(llmqType, pindexStart, poolSize); + if (utils::IsQuorumRotationEnabled(llmq_params, pindexStart)) { + auto quorums = quorum_manager.ScanQuorums(llmq_params.type, pindexStart, poolSize); if (quorums.empty()) { return nullptr; } //log2 int - int n = std::log2(GetLLMQParams(llmqType).signingActiveQuorumCount); + int n = std::log2(llmq_params.signingActiveQuorumCount); //Extract last 64 bits of selectionHash uint64_t b = selectionHash.GetUint64(3); //Take last n bits of b @@ -1021,7 +1023,7 @@ CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType } return *itQuorum; } else { - auto quorums = quorum_manager.ScanQuorums(llmqType, pindexStart, poolSize); + auto quorums = quorum_manager.ScanQuorums(llmq_params.type, pindexStart, poolSize); if (quorums.empty()) { return nullptr; } @@ -1030,7 +1032,7 @@ CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType scores.reserve(quorums.size()); for (const auto i : irange::range(quorums.size())) { CHashWriter h(SER_NETWORK, 0); - h << llmqType; + h << llmq_params.type; h << quorums[i]->qc->quorumHash; h << selectionHash; scores.emplace_back(h.GetHash(), i); @@ -1042,7 +1044,9 @@ CQuorumCPtr CSigningManager::SelectQuorumForSigning(Consensus::LLMQType llmqType bool CSigningManager::VerifyRecoveredSig(Consensus::LLMQType llmqType, const CQuorumManager& quorum_manager, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig, const int signOffset) { - auto quorum = SelectQuorumForSigning(llmqType, quorum_manager, id, signedAtHeight, signOffset); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + auto quorum = SelectQuorumForSigning(llmq_params_opt.value(), quorum_manager, id, signedAtHeight, signOffset); if (!quorum) { return false; } diff --git a/src/llmq/signing.h b/src/llmq/signing.h index 58d96afa20..d62516f981 100644 --- a/src/llmq/signing.h +++ b/src/llmq/signing.h @@ -223,7 +223,7 @@ public: bool GetVoteForId(Consensus::LLMQType llmqType, const uint256& id, uint256& msgHashRet) const; static std::vector GetActiveQuorumSet(Consensus::LLMQType llmqType, int signHeight); - static CQuorumCPtr SelectQuorumForSigning(Consensus::LLMQType llmqType, const CQuorumManager& quorum_manager, const uint256& selectionHash, int signHeight = -1 /*chain tip*/, int signOffset = SIGN_HEIGHT_OFFSET); + static CQuorumCPtr SelectQuorumForSigning(const Consensus::LLMQParams& llmq_params, const CQuorumManager& quorum_manager, const uint256& selectionHash, int signHeight = -1 /*chain tip*/, int signOffset = SIGN_HEIGHT_OFFSET); // Verifies a recovered sig that was signed while the chain tip was at signedAtTip static bool VerifyRecoveredSig(Consensus::LLMQType llmqType, const CQuorumManager& quorum_manager, int signedAtHeight, const uint256& id, const uint256& msgHash, const CBLSSignature& sig, int signOffset = SIGN_HEIGHT_OFFSET); diff --git a/src/llmq/signing_shares.cpp b/src/llmq/signing_shares.cpp index 93f1640759..b5ce27e480 100644 --- a/src/llmq/signing_shares.cpp +++ b/src/llmq/signing_shares.cpp @@ -96,7 +96,9 @@ std::string CBatchedSigShares::ToInvString() const static void InitSession(CSigSharesNodeState::Session& s, const uint256& signHash, CSigBase from) { - const auto& llmq_params = GetLLMQParams((Consensus::LLMQType)from.getLlmqType()); + const auto& llmq_params_opt = GetLLMQParams((Consensus::LLMQType)from.getLlmqType()); + assert(llmq_params_opt.has_value()); + const auto& llmq_params = llmq_params_opt.value(); s.llmqType = from.getLlmqType(); s.quorumHash = from.getQuorumHash(); @@ -296,7 +298,7 @@ void CSigSharesManager::ProcessMessage(const CNode* pfrom, const std::string& ms bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode* pfrom, const CSigSesAnn& ann) { auto llmqType = ann.getLlmqType(); - if (!Params().HasLLMQ(llmqType)) { + if (!GetLLMQParams(llmqType).has_value()) { return false; } if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) { @@ -327,7 +329,8 @@ bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode* pfrom, const CSigSe bool CSigSharesManager::VerifySigSharesInv(Consensus::LLMQType llmqType, const CSigSharesInv& inv) { - return inv.inv.size() == size_t(GetLLMQParams(llmqType).size); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + return llmq_params_opt.has_value() && (inv.inv.size() == size_t(llmq_params_opt->size)); } bool CSigSharesManager::ProcessMessageSigSharesInv(const CNode* pfrom, const CSigSharesInv& inv) @@ -899,7 +902,9 @@ void CSigSharesManager::CollectSigSharesToRequest(std::unordered_mapsize); } inv.inv[k.second] = true; @@ -1046,7 +1051,9 @@ void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_mapgetLlmqType()).size); + const auto& llmq_params_opt = GetLLMQParams(sigShare->getLlmqType()); + assert(llmq_params_opt.has_value()); + inv.Init(llmq_params_opt->size); } inv.inv[quorumMember] = true; session.knows.inv[quorumMember] = true; diff --git a/src/llmq/snapshot.cpp b/src/llmq/snapshot.cpp index dee8cc1a81..9c84d1a5e5 100644 --- a/src/llmq/snapshot.cpp +++ b/src/llmq/snapshot.cpp @@ -168,8 +168,10 @@ bool BuildQuorumRotationInfo(const CGetQuorumRotationInfo& request, CQuorumRotat Consensus::LLMQType llmqType = utils::GetInstantSendLLMQType(qman, blockIndex); // Since the returned quorums are in reversed order, the most recent one is at index 0 - const Consensus::LLMQParams& llmqParams = GetLLMQParams(llmqType); - const int cycleLength = llmqParams.dkgInterval; + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + + const int cycleLength = llmq_params_opt->dkgInterval; constexpr int workDiff = 8; const CBlockIndex* hBlockIndex = blockIndex->GetAncestor(blockIndex->nHeight - (blockIndex->nHeight % cycleLength)); @@ -307,7 +309,7 @@ bool BuildQuorumRotationInfo(const CGetQuorumRotationInfo& request, CQuorumRotat } response.lastCommitmentPerIndex.push_back(*qc); - int quorumCycleStartHeight = obj.second->nHeight - (obj.second->nHeight % llmqParams.dkgInterval); + int quorumCycleStartHeight = obj.second->nHeight - (obj.second->nHeight % llmq_params_opt->dkgInterval); snapshotHeightsNeeded.insert(quorumCycleStartHeight - cycleLength); snapshotHeightsNeeded.insert(quorumCycleStartHeight - 2 * cycleLength); snapshotHeightsNeeded.insert(quorumCycleStartHeight - 3 * cycleLength); diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index c06d19cfc9..a3a9f81c04 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -23,6 +23,8 @@ #include +static constexpr int TESTNET_LLMQ_25_67_ACTIVATION_HEIGHT = 847000; + namespace llmq { @@ -36,7 +38,7 @@ namespace utils void PreComputeQuorumMembers(const CBlockIndex* pQuorumBaseBlockIndex, bool reset_cache) { for (const Consensus::LLMQParams& params : GetEnabledQuorumParams(pQuorumBaseBlockIndex->pprev)) { - if (IsQuorumRotationEnabled(params.type, pQuorumBaseBlockIndex) && (pQuorumBaseBlockIndex->nHeight % params.dkgInterval == 0)) { + if (IsQuorumRotationEnabled(params, pQuorumBaseBlockIndex) && (pQuorumBaseBlockIndex->nHeight % params.dkgInterval == 0)) { GetAllQuorumMembers(params.type, pQuorumBaseBlockIndex, reset_cache); } } @@ -64,7 +66,11 @@ std::vector GetAllQuorumMembers(Consensus::LLMQType llmqTy } } - if (IsQuorumRotationEnabled(llmqType, pQuorumBaseBlockIndex)) { + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + const auto& llmq_params = llmq_params_opt.value(); + + if (IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex)) { if (LOCK(cs_indexed_members); mapIndexedQuorumMembers.empty()) { InitQuorumsCache(mapIndexedQuorumMembers); } @@ -77,9 +83,8 @@ std::vector GetAllQuorumMembers(Consensus::LLMQType llmqTy * Quorum Q with quorumIndex is created at height CycleQuorumBaseBlock + quorumIndex */ - const Consensus::LLMQParams& llmqParams = GetLLMQParams(llmqType); - int quorumIndex = pQuorumBaseBlockIndex->nHeight % llmqParams.dkgInterval; - if (quorumIndex >= llmqParams.signingActiveQuorumCount) { + int quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval; + if (quorumIndex >= llmq_params.signingActiveQuorumCount) { return {}; } int cycleQuorumBaseHeight = pQuorumBaseBlockIndex->nHeight - quorumIndex; @@ -98,7 +103,7 @@ std::vector GetAllQuorumMembers(Consensus::LLMQType llmqTy return quorumMembers; } - auto q = ComputeQuorumMembersByQuarterRotation(llmqType, pCycleQuorumBaseBlockIndex); + auto q = ComputeQuorumMembersByQuarterRotation(llmq_params, pCycleQuorumBaseBlockIndex); LOCK(cs_indexed_members); for (int i = 0; i < static_cast(q.size()); ++i) { mapIndexedQuorumMembers[llmqType].insert(std::make_pair(pCycleQuorumBaseBlockIndex->GetBlockHash(), i), q[i]); @@ -118,13 +123,13 @@ std::vector ComputeQuorumMembers(Consensus::LLMQType llmqT { auto allMns = deterministicMNManager->GetListForBlock(pQuorumBaseBlockIndex); auto modifier = ::SerializeHash(std::make_pair(llmqType, pQuorumBaseBlockIndex->GetBlockHash())); - return allMns.CalculateQuorum(GetLLMQParams(llmqType).size, modifier); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + return allMns.CalculateQuorum(llmq_params_opt->size, modifier); } -std::vector> ComputeQuorumMembersByQuarterRotation(Consensus::LLMQType llmqType, const CBlockIndex* pCycleQuorumBaseBlockIndex) +std::vector> ComputeQuorumMembersByQuarterRotation(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pCycleQuorumBaseBlockIndex) { - const Consensus::LLMQParams& llmqParams = GetLLMQParams(llmqType); - const int cycleLength = llmqParams.dkgInterval; assert(pCycleQuorumBaseBlockIndex->nHeight % cycleLength == 0); @@ -134,7 +139,7 @@ std::vector> ComputeQuorumMembersByQuarterRota LOCK(deterministicMNManager->cs); const CBlockIndex* pWorkBlockIndex = pCycleQuorumBaseBlockIndex->GetAncestor(pCycleQuorumBaseBlockIndex->nHeight - 8); auto allMns = deterministicMNManager->GetListForBlock(pWorkBlockIndex); - LogPrint(BCLog::LLMQ, "ComputeQuorumMembersByQuarterRotation llmqType[%d] nHeight[%d] allMns[%d]\n", static_cast(llmqType), pCycleQuorumBaseBlockIndex->nHeight, allMns.GetValidMNsCount()); + LogPrint(BCLog::LLMQ, "ComputeQuorumMembersByQuarterRotation llmqType[%d] nHeight[%d] allMns[%d]\n", static_cast(llmqParams.type), pCycleQuorumBaseBlockIndex->nHeight, allMns.GetValidMNsCount()); PreviousQuorumQuarters previousQuarters = GetPreviousQuorumQuarterMembers(llmqParams, pBlockHMinusCIndex, pBlockHMinus2CIndex, pBlockHMinus3CIndex, pCycleQuorumBaseBlockIndex->nHeight); @@ -591,16 +596,16 @@ bool IsQuorumPoseEnabled(Consensus::LLMQType llmqType) return EvalSpork(llmqType, sporkManager->GetSporkValue(SPORK_23_QUORUM_POSE)); } -bool IsQuorumRotationEnabled(Consensus::LLMQType llmqType, const CBlockIndex* pindex) +bool IsQuorumRotationEnabled(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pindex) { assert(pindex); - if (!GetLLMQParams(llmqType).useRotation) { + if (!llmqParams.useRotation) { return false; } LOCK(cs_llmq_vbc); - int cycleQuorumBaseHeight = pindex->nHeight - (pindex->nHeight % GetLLMQParams(llmqType).dkgInterval); + int cycleQuorumBaseHeight = pindex->nHeight - (pindex->nHeight % llmqParams.dkgInterval); if (cycleQuorumBaseHeight < 1) { return false; } @@ -863,7 +868,9 @@ bool IsQuorumActive(Consensus::LLMQType llmqType, const CQuorumManager& qman, co // sig shares and recovered sigs are only accepted from recent/active quorums // we allow one more active quorum as specified in consensus, as otherwise there is a small window where things could // fail while we are on the brink of a new quorum - auto quorums = qman.ScanQuorums(llmqType, GetLLMQParams(llmqType).keepOldConnections); + const auto& llmq_params_opt = GetLLMQParams(llmqType); + assert(llmq_params_opt.has_value()); + auto quorums = qman.ScanQuorums(llmqType, llmq_params_opt->keepOldConnections); return ranges::any_of(quorums, [&quorumHash](const auto& q){ return q->qc->quorumHash == quorumHash; }); } @@ -915,6 +922,11 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, const CQuorumMana } break; } + case Consensus::LLMQType::LLMQ_25_67: + if (pindex->nHeight < TESTNET_LLMQ_25_67_ACTIVATION_HEIGHT) { + return false; + } + break; default: throw std::runtime_error(strprintf("%s: Unknown LLMQ type %d", __func__, static_cast(llmqType))); } @@ -1018,7 +1030,7 @@ template void InitQuorumsCache GetLLMQParams(Consensus::LLMQType llmqType) { return Params().GetLLMQ(llmqType); } diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 35cfcae5eb..edfe9c6b95 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -59,7 +59,7 @@ std::vector GetAllQuorumMembers(Consensus::LLMQType llmqTy void PreComputeQuorumMembers(const CBlockIndex* pQuorumBaseBlockIndex, bool reset_cache = false); static std::vector ComputeQuorumMembers(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex); -static std::vector> ComputeQuorumMembersByQuarterRotation(Consensus::LLMQType llmqType, const CBlockIndex* pCycleQuorumBaseBlockIndex); +static std::vector> ComputeQuorumMembersByQuarterRotation(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pCycleQuorumBaseBlockIndex); static std::vector> BuildNewQuorumQuarterMembers(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pQuorumBaseBlockIndex, const PreviousQuorumQuarters& quarters); @@ -89,7 +89,7 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, const CQuorumMana std::vector GetEnabledQuorumTypes(const CBlockIndex* pindex); std::vector> GetEnabledQuorumParams(const CBlockIndex* pindex); -bool IsQuorumRotationEnabled(Consensus::LLMQType llmqType, const CBlockIndex* pindex); +bool IsQuorumRotationEnabled(const Consensus::LLMQParams& llmqParams, const CBlockIndex* pindex); Consensus::LLMQType GetInstantSendLLMQType(const CQuorumManager& qman, const CBlockIndex* pindex); Consensus::LLMQType GetInstantSendLLMQType(bool deterministic); bool IsDIP0024Active(const CBlockIndex* pindex); @@ -139,7 +139,7 @@ void InitQuorumsCache(CacheType& cache); } // namespace utils -const Consensus::LLMQParams& GetLLMQParams(Consensus::LLMQType llmqType); +[[ nodiscard ]] const std::optional GetLLMQParams(Consensus::LLMQType llmqType); } // namespace llmq diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index 58fa0817d2..98edcf99be 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -70,15 +70,16 @@ static UniValue quorum_list(const JSONRPCRequest& request) CBlockIndex* pindexTip = WITH_LOCK(cs_main, return ::ChainActive().Tip()); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pindexTip)) { - const auto& llmq_params = llmq::GetLLMQParams(type); + const auto& llmq_params_opt = llmq::GetLLMQParams(type); + CHECK_NONFATAL(llmq_params_opt.has_value()); UniValue v(UniValue::VARR); - auto quorums = llmq_ctx.qman->ScanQuorums(type, pindexTip, count > -1 ? count : llmq_params.signingActiveQuorumCount); + auto quorums = llmq_ctx.qman->ScanQuorums(type, pindexTip, count > -1 ? count : llmq_params_opt->signingActiveQuorumCount); for (const auto& q : quorums) { v.push_back(q->qc->quorumHash.ToString()); } - ret.pushKV(std::string(llmq_params.name), v); + ret.pushKV(std::string(llmq_params_opt->name), v); } return ret; @@ -135,7 +136,9 @@ static UniValue quorum_list_extended(const JSONRPCRequest& request) CBlockIndex* pblockindex = nHeight != -1 ? WITH_LOCK(cs_main, return ::ChainActive()[nHeight]) : WITH_LOCK(cs_main, return ::ChainActive().Tip()); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pblockindex)) { - const auto& llmq_params = llmq::GetLLMQParams(type); + const auto& llmq_params_opt = llmq::GetLLMQParams(type); + CHECK_NONFATAL(llmq_params_opt.has_value()); + const auto& llmq_params = llmq_params_opt.value(); UniValue v(UniValue::VARR); auto quorums = llmq_ctx.qman->ScanQuorums(type, pblockindex, llmq_params.signingActiveQuorumCount); @@ -222,7 +225,7 @@ static UniValue quorum_info(const JSONRPCRequest& request) quorum_info_help(request); Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType"); - if (!Params().HasLLMQ(llmqType)) { + if (!llmq::GetLLMQParams(llmqType).has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -282,8 +285,10 @@ static UniValue quorum_dkgstatus(const JSONRPCRequest& request) UniValue minableCommitments(UniValue::VARR); UniValue quorumArrConnections(UniValue::VARR); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pindexTip)) { - const auto& llmq_params = llmq::GetLLMQParams(type); - bool rotation_enabled = llmq::utils::IsQuorumRotationEnabled(type, pindexTip); + const auto& llmq_params_opt = llmq::GetLLMQParams(type); + CHECK_NONFATAL(llmq_params_opt.has_value()); + const auto& llmq_params = llmq_params_opt.value(); + bool rotation_enabled = llmq::utils::IsQuorumRotationEnabled(llmq_params, pindexTip); size_t quorums_num = rotation_enabled ? llmq_params.signingActiveQuorumCount : 1; for (int quorumIndex = 0; quorumIndex < quorums_num; ++quorumIndex) { @@ -386,12 +391,13 @@ static UniValue quorum_memberof(const JSONRPCRequest& request) UniValue result(UniValue::VARR); for (const auto& type : llmq::utils::GetEnabledQuorumTypes(pindexTip)) { - const auto& llmq_params = llmq::GetLLMQParams(type); - size_t count = llmq_params.signingActiveQuorumCount; + const auto& llmq_params_opt = llmq::GetLLMQParams(type); + CHECK_NONFATAL(llmq_params_opt.has_value()); + size_t count = llmq_params_opt->signingActiveQuorumCount; if (scanQuorumsCount != -1) { count = (size_t)scanQuorumsCount; } - auto quorums = llmq_ctx.qman->ScanQuorums(llmq_params.type, count); + auto quorums = llmq_ctx.qman->ScanQuorums(llmq_params_opt->type, count); for (auto& quorum : quorums) { if (quorum->IsMember(dmn->proTxHash)) { auto json = BuildQuorumInfo(quorum, false, false); @@ -508,7 +514,8 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request) LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType"); - if (!Params().HasLLMQ(llmqType)) { + const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); + if (!llmq_params_opt.has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -530,7 +537,7 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request) llmq::CQuorumCPtr pQuorum; if (quorumHash.IsNull()) { - pQuorum = llmq_ctx.sigman->SelectQuorumForSigning(llmqType, *llmq_ctx.qman, id); + pQuorum = llmq_ctx.sigman->SelectQuorumForSigning(llmq_params_opt.value(), *llmq_ctx.qman, id); } else { pQuorum = llmq_ctx.qman->GetQuorum(llmqType, quorumHash); } @@ -568,7 +575,7 @@ static UniValue quorum_sigs_cmd(const JSONRPCRequest& request) signHeight = ParseInt32V(request.params[5], "signHeight"); } // First check against the current active set, if it fails check against the last active set - int signOffset{llmq::GetLLMQParams(llmqType).dkgInterval}; + int signOffset{llmq_params_opt->dkgInterval}; return llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, 0) || llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, msgHash, sig, signOffset); } else { @@ -619,7 +626,8 @@ static UniValue quorum_selectquorum(const JSONRPCRequest& request) quorum_selectquorum_help(request); Consensus::LLMQType llmqType = (Consensus::LLMQType)ParseInt32V(request.params[0], "llmqType"); - if (!Params().HasLLMQ(llmqType)) { + const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); + if (!llmq_params_opt.has_value()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "invalid LLMQ type"); } @@ -628,7 +636,7 @@ static UniValue quorum_selectquorum(const JSONRPCRequest& request) UniValue ret(UniValue::VOBJ); LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - auto quorum = llmq_ctx.sigman->SelectQuorumForSigning(llmqType, *llmq_ctx.qman, id); + auto quorum = llmq_ctx.sigman->SelectQuorumForSigning(llmq_params_opt.value(), *llmq_ctx.qman, id); if (!quorum) { throw JSONRPCError(RPC_MISC_ERROR, "no quorums active"); } @@ -944,7 +952,9 @@ static UniValue verifyislock(const JSONRPCRequest& request) auto llmqType = llmq::utils::GetInstantSendLLMQType(*llmq_ctx.qman, pBlockIndex); // First check against the current active set, if it fails check against the last active set - int signOffset{llmq::GetLLMQParams(llmqType).dkgInterval}; + const auto& llmq_params_opt = llmq::GetLLMQParams(llmqType); + CHECK_NONFATAL(llmq_params_opt.has_value()); + int signOffset{llmq_params_opt->dkgInterval}; return llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, 0) || llmq_ctx.sigman->VerifyRecoveredSig(llmqType, *llmq_ctx.qman, signHeight, id, txid, sig, signOffset); }