From a900e3f6c64723c373753ea0a0ced3022fec8e05 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 28 Dec 2022 14:41:52 +0200 Subject: [PATCH 01/64] Added v20 HF --- src/chainparams.cpp | 33 +++++++++++++++++++++++++++++++++ src/consensus/params.h | 3 ++- src/llmq/utils.cpp | 8 ++++++++ src/llmq/utils.h | 2 ++ src/versionbitsinfo.cpp | 5 +++++ 5 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c9dde2425983..f98ad15ab75f 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -255,6 +255,15 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 2420; // 60% of 4032 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods + consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 19999999999; // TODO: To be determined later + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 4032; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 3226; // 80% of 4032 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 2420; // 60% of 4032 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods + + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000007d31aeabf4a72ce1fbaa"); // 1796500 @@ -491,6 +500,14 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 60; // 60% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods + consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 19999999999; // TODO: To be determined later + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 80; // 80% of 100 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68c04d48c0c9c"); // 808000 @@ -699,6 +716,14 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 60; // 60% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods + consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 1661990400; // Sep 1st, 2022 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 100; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 80; // 80% of 100 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000"); @@ -949,6 +974,14 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 180; // 60% of 300 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods + consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 300; + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 240; // 80% of 300 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 180; // 60% of 300 + consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index b538c7726c88..df0a2501b441 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -23,7 +23,8 @@ enum DeploymentPos { DEPLOYMENT_REALLOC, // Deployment of Block Reward Reallocation DEPLOYMENT_DIP0020, // Deployment of DIP0020, DIP0021 and LMQ_100_67 quorums DEPLOYMENT_DIP0024, // Deployment of DIP0024 (Quorum Rotation) and decreased governance proposal fee - DEPLOYMENT_V19, // Deployment of Basic BLS, AssetLocks, EHF + DEPLOYMENT_V19, // Deployment of Basic BLS, AssetLocks + DEPLOYMENT_V20, // Deployment of 4K HPMN, EHF // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index af3f5524b4af..211a19446d4c 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -662,6 +662,14 @@ const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex) return pindex->GetAncestor(nHeight); } +bool IsV20Active(const CBlockIndex* pindex) +{ + assert(pindex); + + LOCK(cs_llmq_vbc); + return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, llmq_versionbitscache) == ThresholdState::ACTIVE; +} + bool IsInstantSendLLMQTypeShared() { if (Params().GetConsensus().llmqTypeInstantSend == Params().GetConsensus().llmqTypeChainLocks || diff --git a/src/llmq/utils.h b/src/llmq/utils.h index a29a35f62ed2..d13059a2abe6 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -85,6 +85,8 @@ Consensus::LLMQType GetInstantSendLLMQType(bool deterministic); bool IsDIP0024Active(const CBlockIndex* pindex); bool IsV19Active(const CBlockIndex* pindex); const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex); +bool IsV20Active(const CBlockIndex* pindex); +static bool IsInstantSendLLMQTypeShared(); /// Returns the state of `-llmq-data-recovery` bool QuorumDataRecoveryEnabled(); diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp index fb94fa7e3962..4f57ab5ff5b6 100644 --- a/src/versionbitsinfo.cpp +++ b/src/versionbitsinfo.cpp @@ -56,5 +56,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/"v19", /*.gbt_force =*/true, /*.check_mn_protocol =*/false, + }, + { + /*.name =*/"v20", + /*.gbt_force =*/true, + /*.check_mn_protocol =*/false, } }; From 03090719405a9a7516d457092fd6a5a3ed63b852 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 10 Oct 2022 21:12:14 +0300 Subject: [PATCH 02/64] Introduction of HighPerformanceMasternode --- src/evo/deterministicmns.cpp | 26 ++++++++++++++++++-------- src/evo/deterministicmns.h | 14 +++++++++++--- src/evo/providertx.h | 4 +++- src/llmq/utils.cpp | 7 ++++++- src/llmq/utils.h | 1 + src/rpc/evo.cpp | 5 ++++- 6 files changed, 43 insertions(+), 14 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 2b9770191df9..bd46479d27e5 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -213,9 +213,9 @@ std::vector CDeterministicMNList::GetProjectedMNPayees(int return result; } -std::vector CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier) const +std::vector CDeterministicMNList::CalculateQuorum(size_t maxSize, const uint256& modifier, const bool onlyHighPerformanceMasternodes) const { - auto scores = CalculateScores(modifier); + auto scores = CalculateScores(modifier, onlyHighPerformanceMasternodes); // sort is descending order std::sort(scores.rbegin(), scores.rend(), [](const std::pair& a, const std::pair& b) { @@ -235,7 +235,7 @@ std::vector CDeterministicMNList::CalculateQuorum(size_t m return result; } -std::vector> CDeterministicMNList::CalculateScores(const uint256& modifier) const +std::vector> CDeterministicMNList::CalculateScores(const uint256& modifier, const bool onlyHighPerformanceMasternodes) const { std::vector> scores; scores.reserve(GetAllMNsCount()); @@ -245,6 +245,10 @@ std::vector> CDeterministicMNList // future quorums return; } + if (onlyHighPerformanceMasternodes) { + if (dmn->type != CDeterministicMN::MasternodeType::HighPerformance) + return; + } // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN // Please note that this is not a double-sha256 but a single-sha256 // The first part is already precalculated (confirmedHashWithProRegTxHash) @@ -695,7 +699,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } - auto dmn = std::make_shared(newList.GetTotalRegisteredCount()); + auto dmn = std::make_shared(newList.GetTotalRegisteredCount(), proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE); dmn->proTxHash = tx.GetHash(); // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself @@ -706,7 +710,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } Coin coin; - if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != 1000 * COIN)) { + CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE ? 4000 * COIN : 1000 * COIN; + if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral)) { // should actually never get to this point as CheckProRegTx should have handled this case. // We do this additional check nevertheless to be 100% sure return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-collateral"); @@ -1013,7 +1018,10 @@ bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, u if (proTx.collateralOutpoint.n >= tx->vout.size() || proTx.collateralOutpoint.n != n) { return false; } - if (tx->vout[n].nValue != 1000 * COIN) { + + CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE ? 4000 * COIN : 1000 * COIN; + + if (tx->vout[n].nValue != expectedCollateral) { return false; } return true; @@ -1156,9 +1164,11 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid const PKHash *keyForPayloadSig = nullptr; COutPoint collateralOutpoint; + CAmount expectedCollateral = ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE ? 4000 * COIN : 1000 * COIN; + if (!ptx.collateralOutpoint.hash.IsNull()) { Coin coin; - if (!view.GetCoin(ptx.collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != 1000 * COIN) { + if (!view.GetCoin(ptx.collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral) { return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral"); } @@ -1178,7 +1188,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid if (ptx.collateralOutpoint.n >= tx.vout.size()) { return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral-index"); } - if (tx.vout[ptx.collateralOutpoint.n].nValue != 1000 * COIN) { + if (tx.vout[ptx.collateralOutpoint.n].nValue != expectedCollateral) { return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-collateral"); } diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 1de7edc8cce1..28044f3c9954 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -41,8 +41,9 @@ class CDeterministicMN public: CDeterministicMN() = delete; // no default constructor, must specify internalId - explicit CDeterministicMN(uint64_t _internalId) : internalId(_internalId) + explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : internalId(_internalId) { + highPerformanceMasternode ? type = MasternodeType::HighPerformance : MasternodeType::Regular; // only non-initial values assert(_internalId != std::numeric_limits::max()); } @@ -59,9 +60,16 @@ class CDeterministicMN s >> *this; } + enum MasternodeType + { + Regular, + HighPerformance + }; + uint256 proTxHash; COutPoint collateralOutpoint; uint16_t nOperatorReward{0}; + MasternodeType type; std::shared_ptr pdmnState; template @@ -302,8 +310,8 @@ class CDeterministicMNList * @param modifier * @return */ - [[nodiscard]] std::vector CalculateQuorum(size_t maxSize, const uint256& modifier) const; - [[nodiscard]] std::vector> CalculateScores(const uint256& modifier) const; + [[nodiscard]] std::vector CalculateQuorum(size_t maxSize, const uint256& modifier, const bool onlyHighPerformanceMasternodes = false) const; + [[nodiscard]] std::vector> CalculateScores(const uint256& modifier, const bool onlyHighPerformanceMasternodes) const; /** * Calculates the maximum penalty which is allowed at the height of this MN list. It is dynamic and might change diff --git a/src/evo/providertx.h b/src/evo/providertx.h index e8f84ec376fe..b6cb4986b1de 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -26,6 +26,8 @@ class CProRegTx static constexpr auto SPECIALTX_TYPE = TRANSACTION_PROVIDER_REGISTER; static constexpr uint16_t LEGACY_BLS_VERSION = 1; static constexpr uint16_t BASIC_BLS_VERSION = 2; + static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; + static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; [[nodiscard]] static constexpr auto GetVersion(const bool is_basic_scheme_active) -> uint16_t { @@ -33,7 +35,7 @@ class CProRegTx } uint16_t nVersion{LEGACY_BLS_VERSION}; // message version - uint16_t nType{0}; // only 0 supported for now + uint16_t nType{TYPE_REGULAR_MASTERNODE}; uint16_t nMode{0}; // only 0 supported for now COutPoint collateralOutpoint{uint256(), (uint32_t)-1}; // if hash is null, we refer to a ProRegTx output CService addr; diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 211a19446d4c..df512592794a 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -131,7 +131,7 @@ 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); + return allMns.CalculateQuorum(GetLLMQParams(llmqType).size, modifier, IsPlatformLLMQType(llmqType)); } std::vector> ComputeQuorumMembersByQuarterRotation(Consensus::LLMQType llmqType, const CBlockIndex* pCycleQuorumBaseBlockIndex) @@ -681,6 +681,11 @@ bool IsInstantSendLLMQTypeShared() return false; } +bool IsPlatformLLMQType(Consensus::LLMQType llmqType) +{ + return Params().GetConsensus().llmqTypePlatform == llmqType; +} + uint256 DeterministicOutboundConnection(const uint256& proTxHash1, const uint256& proTxHash2) { // We need to deterministically select who is going to initiate the connection. The naive way would be to simply diff --git a/src/llmq/utils.h b/src/llmq/utils.h index d13059a2abe6..7ea1db0defe2 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -87,6 +87,7 @@ bool IsV19Active(const CBlockIndex* pindex); const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex); bool IsV20Active(const CBlockIndex* pindex); static bool IsInstantSendLLMQTypeShared(); +static bool IsPlatformLLMQType(Consensus::LLMQType llmqType); /// Returns the state of `-llmq-data-recovery` bool QuorumDataRecoveryEnabled(); diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 10983deca993..d487d7690e11 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -456,7 +456,9 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, size_t paramIdx = 0; - CAmount collateralAmount = 1000 * COIN; + //TODO: Parse request and adapt accordingly + bool high_performance_masternode{false}; + CAmount collateralAmount = high_performance_masternode ? 4000 * COIN : 1000 * COIN; CMutableTransaction tx; tx.nVersion = 3; @@ -467,6 +469,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; else ptx.nVersion = CProRegTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); + ptx.nType = high_performance_masternode ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); From d088d9ff3425914e333c5907151eabf0326bd0f6 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 14 Dec 2022 15:36:55 +0200 Subject: [PATCH 03/64] Added LLMQ_TEST_PLATFORM --- src/chainparams.cpp | 3 ++- src/llmq/params.h | 28 +++++++++++++++++++++++++++- src/llmq/utils.cpp | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index f98ad15ab75f..aaaf2c8234fb 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -1060,10 +1060,11 @@ class CRegTestParams : public CChainParams { AddLLMQ(Consensus::LLMQType::LLMQ_TEST_INSTANTSEND); AddLLMQ(Consensus::LLMQType::LLMQ_TEST_V17); AddLLMQ(Consensus::LLMQType::LLMQ_TEST_DIP0024); + AddLLMQ(Consensus::LLMQType::LLMQ_TEST_PLATFORM); consensus.llmqTypeChainLocks = Consensus::LLMQType::LLMQ_TEST; consensus.llmqTypeInstantSend = Consensus::LLMQType::LLMQ_TEST_INSTANTSEND; consensus.llmqTypeDIP0024InstantSend = Consensus::LLMQType::LLMQ_TEST_DIP0024; - consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_TEST; + consensus.llmqTypePlatform = Consensus::LLMQType::LLMQ_TEST_PLATFORM; consensus.llmqTypeMnhf = Consensus::LLMQType::LLMQ_TEST; UpdateLLMQTestParametersFromArgs(args, Consensus::LLMQType::LLMQ_TEST); diff --git a/src/llmq/params.h b/src/llmq/params.h index 1a2d9029071b..75cb0f3515d2 100644 --- a/src/llmq/params.h +++ b/src/llmq/params.h @@ -32,6 +32,7 @@ enum class LLMQType : uint8_t { // for testing only LLMQ_TEST_DIP0024 = 103, // 4 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used LLMQ_TEST_INSTANTSEND = 104, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestinstantsendparams is used + LLMQ_TEST_PLATFORM = 106, // 4 members, 2 (66%) threshold, one per hour. // for devnets only. rotated version (v2) for devnets LLMQ_DEVNET_DIP0024 = 105 // 8 members, 4 (50%) threshold, one per hour. Params might differ when -llmqdevnetparams is used @@ -106,7 +107,7 @@ struct LLMQParams { }; -static constexpr std::array available_llmqs = { +static constexpr std::array available_llmqs = { /** * llmq_test @@ -208,6 +209,31 @@ static constexpr std::array available_llmqs = { .recoveryMembers = 3, }, + /** + * llmq_test_platform + * This quorum is only used for testing + * + */ + LLMQParams{ + .type = LLMQType::LLMQ_TEST_PLATFORM, + .name = "llmq_test_platform", + .useRotation = false, + .size = 4, + .minSize = 3, + .threshold = 2, + + .dkgInterval = 24, // DKG cycle + .dkgPhaseBlocks = 2, + .dkgMiningWindowStart = 12, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 20, + .dkgBadVotesThreshold = 2, + + .signingActiveQuorumCount = 2, // just a few ones to allow easier testing + + .keepOldConnections = 4, + .recoveryMembers = 3, + }, + /** * llmq_devnet * This quorum is only used for testing on devnets diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index df512592794a..1ee1d996ec6f 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -943,6 +943,7 @@ bool IsQuorumTypeEnabledInternal(Consensus::LLMQType llmqType, const CQuorumMana break; } case Consensus::LLMQType::LLMQ_TEST: + case Consensus::LLMQType::LLMQ_TEST_PLATFORM: case Consensus::LLMQType::LLMQ_400_60: case Consensus::LLMQType::LLMQ_400_85: break; From 8e925c241e2d96f6cb4000696faf4d66abae78b9 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sat, 17 Dec 2022 17:47:17 +0200 Subject: [PATCH 04/64] implementation --- src/evo/deterministicmns.cpp | 2 +- src/evo/deterministicmns.h | 19 +- src/evo/providertx.cpp | 2 +- src/llmq/params.h | 34 ++-- src/rpc/evo.cpp | 168 ++++++++++++++++++ src/rpc/masternode.cpp | 1 + test/functional/feature_llmq_hpmn.py | 82 +++++++++ .../test_framework/test_framework.py | 38 ++-- test/functional/test_runner.py | 1 + 9 files changed, 306 insertions(+), 41 deletions(-) create mode 100755 test/functional/feature_llmq_hpmn.py diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index bd46479d27e5..a76784302101 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -246,7 +246,7 @@ std::vector> CDeterministicMNList return; } if (onlyHighPerformanceMasternodes) { - if (dmn->type != CDeterministicMN::MasternodeType::HighPerformance) + if (dmn->nType != CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) return; } // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 28044f3c9954..b989793c997c 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -40,10 +40,13 @@ class CDeterministicMN uint64_t internalId{std::numeric_limits::max()}; public: + static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; + static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; + CDeterministicMN() = delete; // no default constructor, must specify internalId explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : internalId(_internalId) { - highPerformanceMasternode ? type = MasternodeType::HighPerformance : MasternodeType::Regular; + highPerformanceMasternode ? nType = TYPE_HIGH_PERFORMANCE_MASTERNODE : TYPE_REGULAR_MASTERNODE; // only non-initial values assert(_internalId != std::numeric_limits::max()); } @@ -60,16 +63,10 @@ class CDeterministicMN s >> *this; } - enum MasternodeType - { - Regular, - HighPerformance - }; - uint256 proTxHash; COutPoint collateralOutpoint; uint16_t nOperatorReward{0}; - MasternodeType type; + uint16_t nType{TYPE_REGULAR_MASTERNODE};; std::shared_ptr pdmnState; template @@ -82,6 +79,7 @@ class CDeterministicMN READWRITE(collateralOutpoint); READWRITE(nOperatorReward); READWRITE(pdmnState); + READWRITE(nType); } template @@ -216,6 +214,11 @@ class CDeterministicMNList return ranges::count_if(mnMap, [](const auto& p){ return IsMNValid(*p.second); }); } + [[nodiscard]] size_t GetAllHPMNsCount() const + { + return ranges::count_if(mnMap, [](const auto& p){ return p.second->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE;}); + } + /** * Execute a callback on all masternodes in the mnList. This will pass a reference * of each masternode to the callback function. This should be preferred over ForEachMNShared. diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index edb28c20aedb..4439e673a8f4 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -14,7 +14,7 @@ maybe_error CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const if (nVersion == 0 || nVersion > GetVersion(is_bls_legacy_scheme)) { return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"}; } - if (nType != 0) { + if (nType != TYPE_REGULAR_MASTERNODE && nType != TYPE_HIGH_PERFORMANCE_MASTERNODE) { return {ValidationInvalidReason::CONSENSUS, "bad-protx-type"}; } if (nMode != 0) { diff --git a/src/llmq/params.h b/src/llmq/params.h index 75cb0f3515d2..5afb316356cb 100644 --- a/src/llmq/params.h +++ b/src/llmq/params.h @@ -215,23 +215,23 @@ static constexpr std::array available_llmqs = { * */ LLMQParams{ - .type = LLMQType::LLMQ_TEST_PLATFORM, - .name = "llmq_test_platform", - .useRotation = false, - .size = 4, - .minSize = 3, - .threshold = 2, - - .dkgInterval = 24, // DKG cycle - .dkgPhaseBlocks = 2, - .dkgMiningWindowStart = 12, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization - .dkgMiningWindowEnd = 20, - .dkgBadVotesThreshold = 2, - - .signingActiveQuorumCount = 2, // just a few ones to allow easier testing - - .keepOldConnections = 4, - .recoveryMembers = 3, + .type = LLMQType::LLMQ_TEST_PLATFORM, + .name = "llmq_test_platform", + .useRotation = false, + .size = 4, + .minSize = 3, + .threshold = 2, + + .dkgInterval = 24, // DKG cycle + .dkgPhaseBlocks = 2, + .dkgMiningWindowStart = 10, // signingActiveQuorumCount + dkgPhaseBlocks * 5 = after finalization + .dkgMiningWindowEnd = 18, + .dkgBadVotesThreshold = 2, + + .signingActiveQuorumCount = 2, // just a few ones to allow easier testing + + .keepOldConnections = 4, + .recoveryMembers = 3, }, /** diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index d487d7690e11..faf611bc3c8c 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -621,6 +621,172 @@ static UniValue protx_register_legacy(const JSONRPCRequest& request) } // handles register, register_prepare and register_fund in one method +static UniValue protx_register_highperf(const JSONRPCRequest& request) +{ + bool isExternalRegister = request.strMethod == "protxregister_highperf"; + bool isFundRegister = request.strMethod == "protxregister_fund_highperf"; + bool isPrepareRegister = request.strMethod == "protxregister_prepare_highperf"; + + if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { + protx_register_fund_help(request); + } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { + protx_register_help(request); + } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { + protx_register_prepare_help(request); + } + + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); + if (!wallet) return NullUniValue; + + if (isExternalRegister || isFundRegister) { + EnsureWalletIsUnlocked(wallet.get()); + } + + size_t paramIdx = 0; + + CAmount collateralAmount = 4000 * COIN; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_REGISTER; + + CProRegTx ptx; + ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + ptx.nVersion = CProRegTx::CURRENT_VERSION; + + if (isFundRegister) { + CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); + if (!IsValidDestination(collateralDest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); + } + CScript collateralScript = GetScriptForDestination(collateralDest); + + CTxOut collateralTxOut(collateralAmount, collateralScript); + tx.vout.emplace_back(collateralTxOut); + + paramIdx++; + } else { + uint256 collateralHash = ParseHashV(request.params[paramIdx], "collateralHash"); + int32_t collateralIndex = ParseInt32V(request.params[paramIdx + 1], "collateralIndex"); + if (collateralHash.IsNull() || collateralIndex < 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex)); + } + + ptx.collateralOutpoint = COutPoint(collateralHash, (uint32_t)collateralIndex); + paramIdx += 2; + + // TODO unlock on failure + LOCK(wallet->cs_wallet); + wallet->LockCoin(ptx.collateralOutpoint); + } + + if (request.params[paramIdx].get_str() != "") { + if (!Lookup(request.params[paramIdx].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { + throw std::runtime_error(strprintf("invalid network address %s", request.params[paramIdx].get_str())); + } + } + + ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address"); + CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address"); + CKeyID keyIDVoting = ptx.keyIDOwner; + + if (request.params[paramIdx + 3].get_str() != "") { + keyIDVoting = ParsePubKeyIDFromAddress(request.params[paramIdx + 3].get_str(), "voting address"); + } + + int64_t operatorReward; + if (!ParseFixedPoint(request.params[paramIdx + 4].getValStr(), 2, &operatorReward)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be a number"); + } + if (operatorReward < 0 || operatorReward > 10000) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be between 0.00 and 100.00"); + } + ptx.nOperatorReward = operatorReward; + + CTxDestination payoutDest = DecodeDestination(request.params[paramIdx + 5].get_str()); + if (!IsValidDestination(payoutDest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); + } + + ptx.pubKeyOperator = pubKeyOperator; + ptx.keyIDVoting = keyIDVoting; + ptx.scriptPayout = GetScriptForDestination(payoutDest); + + if (!isFundRegister) { + // make sure fee calculation works + ptx.vchSig.resize(65); + } + + CTxDestination fundDest = payoutDest; + if (!request.params[paramIdx + 6].isNull()) { + fundDest = DecodeDestination(request.params[paramIdx + 6].get_str()); + if (!IsValidDestination(fundDest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 6].get_str()); + } + + FundSpecialTx(wallet.get(), tx, ptx, fundDest); + UpdateSpecialTxInputsHash(tx, ptx); + + bool fSubmit{true}; + if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 7].isNull()) { + fSubmit = ParseBoolV(request.params[paramIdx + 7], "submit"); + } + + if (isFundRegister) { + uint32_t collateralIndex = (uint32_t) -1; + for (uint32_t i = 0; i < tx.vout.size(); i++) { + if (tx.vout[i].nValue == collateralAmount) { + collateralIndex = i; + break; + } + } + CHECK_NONFATAL(collateralIndex != (uint32_t) -1); + ptx.collateralOutpoint.n = collateralIndex; + + SetTxPayload(tx, ptx); + return SignAndSendSpecialTx(request, tx, fSubmit); + } else { + // referencing external collateral + + Coin coin; + if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort())); + } + CTxDestination txDest; + ExtractDestination(coin.out.scriptPubKey, txDest); + const CKeyID *keyID = std::get_if(&txDest); + if (!keyID) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); + } + + if (isPrepareRegister) { + // external signing with collateral key + ptx.vchSig.clear(); + SetTxPayload(tx, ptx); + + UniValue ret(UniValue::VOBJ); + ret.pushKV("tx", EncodeHexTx(CTransaction(tx))); + ret.pushKV("collateralAddress", EncodeDestination(txDest)); + ret.pushKV("signMessage", ptx.MakeSignString()); + return ret; + } else { + // lets prove we own the collateral + LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command"); + } + + CKey key; + if (!spk_man->GetKey(*keyID, key)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest))); + } + SignSpecialTxPayloadByString(tx, ptx, key); + SetTxPayload(tx, ptx); + return SignAndSendSpecialTx(request, tx, fSubmit); + } + } +} + static UniValue protx_register_submit(const JSONRPCRequest& request) { protx_register_submit_help(request); @@ -1270,6 +1436,8 @@ static UniValue protx(const JSONRPCRequest& request) return protx_register(new_request); } else if (command == "protxregister_legacy" || command == "protxregister_fund_legacy" || command == "protxregister_prepare_legacy") { return protx_register_legacy(new_request); + } else if (command == "protxregister_highperf" || command == "protxregister_fund_highperf" || command == "protxregister_prepare_highperf") { + return protx_register_highperf(new_request); } else if (command == "protxregister_submit") { return protx_register_submit(new_request); } else if (command == "protxupdate_service") { diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 01b848656757..9d58cdde289f 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -666,6 +666,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("address", dmn.pdmnState->addr.ToString()); objMN.pushKV("payee", payeeStr); objMN.pushKV("status", dmnToStatus(dmn)); + objMN.pushKV("type", dmn.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty); objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn)); objMN.pushKV("lastpaidblock", dmn.pdmnState->nLastPaidHeight); diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py new file mode 100755 index 000000000000..4a4a5ecfa933 --- /dev/null +++ b/test/functional/feature_llmq_hpmn.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +# Copyright (c) 2015-2022 The Dash Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +''' +feature_llmq_rotation.py + +Checks LLMQs Quorum Rotation + +''' +from io import BytesIO + +from test_framework.test_framework import DashTestFramework +from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, FromHex, hash256, msg_getmnlistd, \ + QuorumId +from test_framework.mininode import P2PInterface +from test_framework.util import ( + assert_equal, + assert_greater_than_or_equal, + wait_until, +) + + +def intersection(lst1, lst2): + lst3 = [value for value in lst1 if value in lst2] + return lst3 + + +def extract_quorum_members(quorum_info): + return [d['proTxHash'] for d in quorum_info["members"]] + + +class LLMQHPMNTest(DashTestFramework): + def set_test_params(self): + self.set_dash_test_params(9, 8, fast_dip3_enforcement=True, hpmn_count=4) + self.set_dash_llmq_test_params(4, 4) + + def run_test(self): + llmq_type = 106 + llmq_type_name = "llmq_test_platform" + + # Connect all nodes to node1 so that we always have the whole network connected + # Otherwise only masternode connections will be established between nodes, which won't propagate TXs/blocks + # Usually node0 is the one that does this, but in this test we isolate it multiple times + + for i in range(len(self.nodes)): + if i != 1: + self.connect_nodes(i, 0) + + self.activate_dip8() + + self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", 0) + self.wait_for_sporks_same() + + quorum_0_hash = self.mine_quorum(llmq_type_name, llmq_type) + self.test_quorum_members_are_high_performance(llmq_type, quorum_0_hash) + + quorum_1_hash = self.mine_quorum(llmq_type_name, llmq_type) + self.test_quorum_members_are_high_performance(llmq_type, quorum_1_hash) + + quorum_2_hash = self.mine_quorum(llmq_type_name, llmq_type) + self.test_quorum_members_are_high_performance(llmq_type, quorum_2_hash) + + return + + def test_quorum_members_are_high_performance(self, llmq_type, quorum_hash): + quorum_info = self.nodes[0].quorum("info", llmq_type, quorum_hash) + quorum_members = extract_quorum_members(quorum_info) + mninfos_online = self.mninfo.copy() + for qm in quorum_members: + found = False + for mn in mninfos_online: + if mn.proTxHash == qm: + assert_equal(mn.hpmn, True) + found = True + break + assert_equal(found, True) + + +if __name__ == '__main__': + LLMQHPMNTest().main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 348f509338b8..d01e824c674f 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -49,7 +49,7 @@ set_timeout_scale, satoshi_round, wait_until, - get_chain_folder, + get_chain_folder, assert_greater_than_or_equal, ) @@ -826,10 +826,10 @@ def is_zmq_compiled(self): MASTERNODE_COLLATERAL = 1000 - +HIGHPERFORMANCE_MASTERNODE_COLLATERAL = 4000 class MasternodeInfo: - def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout): + def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout, hpmn=False): self.proTxHash = proTxHash self.ownerAddr = ownerAddr self.votingAddr = votingAddr @@ -838,6 +838,7 @@ def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator self.collateral_address = collateral_address self.collateral_txid = collateral_txid self.collateral_vout = collateral_vout + self.hpmn = hpmn class DashTestFramework(BitcoinTestFramework): @@ -852,8 +853,9 @@ def run_test(self): """Tests must override this method to define test logic""" raise NotImplementedError - def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, fast_dip3_enforcement=False): + def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, fast_dip3_enforcement=False, hpmn_count=0): self.mn_count = masterodes_count + self.hpmn_count = hpmn_count self.num_nodes = num_nodes self.mninfo = [] self.setup_clean_chain = True @@ -861,6 +863,7 @@ def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, fas if extra_args is None: extra_args = [[]] * num_nodes assert_equal(len(extra_args), num_nodes) + assert_greater_than_or_equal(masterodes_count, hpmn_count) self.extra_args = [copy.deepcopy(a) for a in extra_args] self.extra_args[0] += ["-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"] self.fast_dip3_enforcement = fast_dip3_enforcement @@ -957,25 +960,28 @@ def prepare_masternodes(self): self.log.info("Preparing %d masternodes" % self.mn_count) rewardsAddr = self.nodes[0].getnewaddress() - for idx in range(0, self.mn_count): - self.prepare_masternode(idx, rewardsAddr) + for idx in range(0, self.hpmn_count): + self.prepare_masternode(idx, rewardsAddr, True) + for idx in range(self.hpmn_count, self.mn_count): + self.prepare_masternode(idx, rewardsAddr, False) self.sync_all() - def prepare_masternode(self, idx, rewardsAddr=None): + def prepare_masternode(self, idx, rewardsAddr=None, hpmn=False): register_fund = (idx % 2) == 0 bls = self.nodes[0].bls('generate') address = self.nodes[0].getnewaddress() + collateral_amount = HIGHPERFORMANCE_MASTERNODE_COLLATERAL if hpmn else MASTERNODE_COLLATERAL txid = None - txid = self.nodes[0].sendtoaddress(address, MASTERNODE_COLLATERAL) + txid = self.nodes[0].sendtoaddress(address, collateral_amount) collateral_vout = 0 if not register_fund: txraw = self.nodes[0].getrawtransaction(txid, True) for vout_idx in range(0, len(txraw["vout"])): vout = txraw["vout"][vout_idx] - if vout["value"] == MASTERNODE_COLLATERAL: + if vout["value"] == collateral_amount: collateral_vout = vout_idx self.nodes[0].lockunspent(False, [{'txid': txid, 'vout': collateral_vout}]) @@ -997,10 +1003,12 @@ def prepare_masternode(self, idx, rewardsAddr=None): if register_fund: # self.nodes[0].lockunspent(True, [{'txid': txid, 'vout': collateral_vout}]) - protx_result = self.nodes[0].protx('register_fund', address, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) + register_fund_rpc = "register_fund_highperf" if hpmn else "register_fund" + protx_result = self.nodes[0].protx(register_fund_rpc, address, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) else: self.nodes[0].generate(1) - protx_result = self.nodes[0].protx('register', txid, collateral_vout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) + register_rpc = "register_highperf" if hpmn else "register" + protx_result = self.nodes[0].protx(register_rpc, txid, collateral_vout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) if submit: proTxHash = protx_result @@ -1013,10 +1021,11 @@ def prepare_masternode(self, idx, rewardsAddr=None): operatorPayoutAddress = self.nodes[0].getnewaddress() self.nodes[0].protx('update_service', proTxHash, ipAndPort, bls['secret'], operatorPayoutAddress, address) - self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout)) + self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout, hpmn)) # self.sync_all() - self.log.info("Prepared masternode %d: collateral_txid=%s, collateral_vout=%d, protxHash=%s" % (idx, txid, collateral_vout, proTxHash)) + mn_type_str = "High performance masternode" if hpmn else "masternode" + self.log.info("Prepared %s %d: collateral_txid=%s, collateral_vout=%d, protxHash=%s" % (mn_type_str, idx, txid, collateral_vout, proTxHash)) def remove_masternode(self, idx): mn = self.mninfo[idx] @@ -1089,7 +1098,8 @@ def setup_network(self): self.add_nodes(1, extra_args=[self.extra_args[0]]) self.start_node(0) self.import_deterministic_coinbase_privkeys() - required_balance = MASTERNODE_COLLATERAL * self.mn_count + 1 + required_balance = HIGHPERFORMANCE_MASTERNODE_COLLATERAL * self.hpmn_count + required_balance += MASTERNODE_COLLATERAL * (self.mn_count - self.hpmn_count) + 1 self.log.info("Generating %d coins" % required_balance) while self.nodes[0].getbalance() < required_balance: self.bump_mocktime(1) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 7ec534660fdd..474da55260cb 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -111,6 +111,7 @@ 'feature_llmq_chainlocks.py', # NOTE: needs dash_hash to pass 'feature_llmq_rotation.py', # NOTE: needs dash_hash to pass 'feature_llmq_connections.py', # NOTE: needs dash_hash to pass + 'feature_llmq_hpmn.py', # NOTE: needs dash_hash to pass 'feature_llmq_simplepose.py', # NOTE: needs dash_hash to pass 'feature_llmq_is_cl_conflicts.py', # NOTE: needs dash_hash to pass 'feature_llmq_is_migration.py', # NOTE: needs dash_hash to pass From eaef8bf04a971c937749fb16f202abcdf346eb33 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sat, 17 Dec 2022 19:11:35 +0200 Subject: [PATCH 05/64] Update feature_new_quorum_type_activation.py --- test/functional/feature_new_quorum_type_activation.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/feature_new_quorum_type_activation.py b/test/functional/feature_new_quorum_type_activation.py index 3535b72fc490..d70aa78a4bef 100755 --- a/test/functional/feature_new_quorum_type_activation.py +++ b/test/functional/feature_new_quorum_type_activation.py @@ -24,17 +24,17 @@ def run_test(self): self.nodes[0].generate(9) assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'started') ql = self.nodes[0].quorum("list") - assert_equal(len(ql), 2) + assert_equal(len(ql), 3) assert "llmq_test_v17" not in ql self.nodes[0].generate(10) assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'locked_in') ql = self.nodes[0].quorum("list") - assert_equal(len(ql), 2) + assert_equal(len(ql), 3) assert "llmq_test_v17" not in ql self.nodes[0].generate(10) assert_equal(get_bip9_status(self.nodes[0], 'dip0020')['status'], 'active') ql = self.nodes[0].quorum("list") - assert_equal(len(ql), 3) + assert_equal(len(ql), 4) assert "llmq_test_v17" in ql From c28cdc36ea8959bc3478f4d2209bdb922495913b Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 21 Dec 2022 20:16:08 +0200 Subject: [PATCH 06/64] Masternodes migration --- src/evo/deterministicmns.cpp | 110 ++++++++++++++++++++++++++++++++++- src/evo/deterministicmns.h | 45 ++++++++------ src/evo/evodb.h | 3 +- src/init.cpp | 5 ++ 4 files changed, 142 insertions(+), 21 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index a76784302101..e77ea4288d8c 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -24,8 +24,8 @@ #include -static const std::string DB_LIST_SNAPSHOT = "dmn_S"; -static const std::string DB_LIST_DIFF = "dmn_D"; +static const std::string DB_LIST_SNAPSHOT = "dmn_S2"; +static const std::string DB_LIST_DIFF = "dmn_D2"; std::unique_ptr deterministicMNManager; @@ -49,6 +49,8 @@ void CDeterministicMN::ToJson(UniValue& obj) const UniValue stateObj; pdmnState->ToJson(stateObj); + obj.pushKV("type", nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); + obj.pushKV("type", nType); obj.pushKV("proTxHash", proTxHash.ToString()); obj.pushKV("collateralHash", collateralOutpoint.hash.ToString()); obj.pushKV("collateralIndex", (int)collateralOutpoint.n); @@ -1082,6 +1084,110 @@ void CDeterministicMNManager::CleanupCache(int nHeight) } } +void CDeterministicMNManager::MigrateDiff(CDBBatch& batch, const CBlockIndex* pindexNext, const CDeterministicMNList& curMNList, CDeterministicMNList& newMNList) +{ + static const std::string DB_OLD_LIST_DIFF = "dmn_D"; + + CDataStream diff_data(SER_DISK, CLIENT_VERSION); + if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindexNext->GetBlockHash()), diff_data)) { + newMNList = curMNList; + newMNList.SetBlockHash(pindexNext->GetBlockHash()); + newMNList.SetHeight(pindexNext->nHeight); + return; + } + + CDeterministicMNListDiff mndiff; + mndiff.Unserialize(diff_data, CDeterministicMN::CURRENT_MN_FORMAT); + + // At this point, all masternodes included in the diff are set as regular masternodes by default. + // We can then write them in evoDB as the new serialisation format includes the type + + batch.Write(std::make_pair(DB_LIST_DIFF, pindexNext->GetBlockHash()), mndiff); + + // applies added/removed MNs + newMNList = curMNList.ApplyDiff(pindexNext, mndiff); +} + +bool CDeterministicMNManager::MigrateDBIfNeeded() +{ + static const std::string DB_OLD_LIST_SNAPSHOT = "dmn_S"; + static const std::string DB_OLD_LIST_DIFF = "dmn_D"; + static const std::string DB_OLD_BEST_BLOCK = "b_b2"; + + LOCK(cs_main); + + LogPrintf("CDeterministicMNManager::%s -- upgrading DB to migrate MN type\n", __func__); + + if (::ChainActive().Tip() == nullptr) { + // should have no records + LogPrintf("CDeterministicMNManager::%s -- Chain empty. evoDB:%d.\n", __func__, m_evoDb.IsEmpty()); + return m_evoDb.IsEmpty(); + } + + if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK)) { + LogPrintf("CDeterministicMNManager::%s -- migration already done. skipping.\n", __func__); + return true; + } + + // Removing the old EVODB_BEST_BLOCK value early results in older version to crash immediately, even if the upgrade + // process is cancelled in-between. But if the new version sees that the old EVODB_BEST_BLOCK is already removed, + // then we must assume that the upgrade process was already running before but was interrupted. + if (::ChainActive().Height() > 1 && !m_evoDb.GetRawDB().Exists(DB_OLD_BEST_BLOCK)) { + LogPrintf("CDeterministicMNManager::%s -- previous migration attempt failed.\n", __func__); + return false; + } + m_evoDb.GetRawDB().Erase(DB_OLD_BEST_BLOCK); + + if (::ChainActive().Height() < Params().GetConsensus().DIP0003Height) { + // not reached DIP3 height yet, so no upgrade needed + LogPrintf("CDeterministicMNManager::%s -- migration not needed. dip3 not reached\n", __func__); + auto dbTx = m_evoDb.BeginTransaction(); + m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash()); + dbTx->Commit(); + return true; + } + + CDBBatch batch(m_evoDb.GetRawDB()); + + CDeterministicMNList curMNList; + curMNList.SetHeight(Params().GetConsensus().DIP0003Height - 1); + curMNList.SetBlockHash(::ChainActive()[Params().GetConsensus().DIP0003Height - 1]->GetBlockHash()); + + for (int nHeight = Params().GetConsensus().DIP0003Height; nHeight <= ::ChainActive().Height(); nHeight++) { + auto pindex = ::ChainActive()[nHeight]; + + CDeterministicMNList newMNList; + MigrateDiff(batch, pindex, curMNList, newMNList); + + if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0) { + batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), newMNList); + m_evoDb.GetRawDB().WriteBatch(batch); + batch.Clear(); + } + + curMNList = newMNList; + } + + m_evoDb.GetRawDB().WriteBatch(batch); + + // Writing EVODB_BEST_BLOCK (which is b_b3 now) marks the DB as upgraded + auto dbTx = m_evoDb.BeginTransaction(); + m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash()); + dbTx->Commit(); + + LogPrintf("CDeterministicMNManager::%s -- done migrating\n", __func__); + + m_evoDb.GetRawDB().Erase(DB_OLD_LIST_DIFF); + m_evoDb.GetRawDB().Erase(DB_OLD_LIST_SNAPSHOT); + + LogPrintf("CDeterministicMNManager::%s -- done cleaning old data\n", __func__); + + m_evoDb.GetRawDB().CompactFull(); + + LogPrintf("CDeterministicMNManager::%s -- done compacting database\n", __func__); + + return true; +} template static bool CheckService(const ProTx& proTx, CValidationState& state) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index b989793c997c..02df3d8272c7 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -43,19 +43,16 @@ class CDeterministicMN static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; + static constexpr uint16_t CURRENT_MN_FORMAT = 0; + static constexpr uint16_t MN_TYPE_FORMAT = 1; + CDeterministicMN() = delete; // no default constructor, must specify internalId explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : internalId(_internalId) { - highPerformanceMasternode ? nType = TYPE_HIGH_PERFORMANCE_MASTERNODE : TYPE_REGULAR_MASTERNODE; + highPerformanceMasternode ? nType = TYPE_HIGH_PERFORMANCE_MASTERNODE : nType = TYPE_REGULAR_MASTERNODE; // only non-initial values assert(_internalId != std::numeric_limits::max()); } - // TODO: can be removed in a future version - CDeterministicMN(CDeterministicMN mn, uint64_t _internalId) : CDeterministicMN(std::move(mn)) { - // only non-initial values - assert(_internalId != std::numeric_limits::max()); - internalId = _internalId; - } template CDeterministicMN(deserialize_type, Stream& s) @@ -66,32 +63,36 @@ class CDeterministicMN uint256 proTxHash; COutPoint collateralOutpoint; uint16_t nOperatorReward{0}; - uint16_t nType{TYPE_REGULAR_MASTERNODE};; + uint16_t nType{TYPE_REGULAR_MASTERNODE}; std::shared_ptr pdmnState; template - inline void SerializationOp(Stream& s, Operation ser_action, bool oldFormat) + inline void SerializationOp(Stream& s, Operation ser_action, const uint8_t format_version) { READWRITE(proTxHash); - if (!oldFormat) { - READWRITE(VARINT(internalId)); - } + READWRITE(VARINT(internalId)); READWRITE(collateralOutpoint); READWRITE(nOperatorReward); READWRITE(pdmnState); - READWRITE(nType); + //TODO: Include check for protocol version, so it won't break clients + if (format_version >= MN_TYPE_FORMAT) { + READWRITE(nType); + } + else { + nType = TYPE_REGULAR_MASTERNODE; + } } template void Serialize(Stream& s) const { - const_cast(this)->SerializationOp(s, CSerActionSerialize(), false); + const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_TYPE_FORMAT); } template - void Unserialize(Stream& s, bool oldFormat = false) + void Unserialize(Stream& s, const uint8_t format_version = MN_TYPE_FORMAT) { - SerializationOp(s, CSerActionUnserialize(), oldFormat); + SerializationOp(s, CSerActionUnserialize(), format_version); } [[nodiscard]] uint64_t GetInternalId() const; @@ -460,14 +461,20 @@ class CDeterministicMNListDiff } template - void Unserialize(Stream& s) + void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT) { updatedMNs.clear(); removedMns.clear(); size_t tmp; uint64_t tmp2; - s >> addedMNs; + tmp = ReadCompactSize(s); + for (size_t i = 0; i < tmp; i++) { + CDeterministicMN mn(0); + mn.Unserialize(s, format_version); + auto dmn = std::make_shared(mn); + addedMNs.push_back(dmn); + } tmp = ReadCompactSize(s); for (size_t i = 0; i < tmp; i++) { CDeterministicMNStateDiff diff; @@ -538,6 +545,8 @@ class CDeterministicMNManager bool IsDIP3Enforced(int nHeight = -1); + void MigrateDiff(CDBBatch& batch, const CBlockIndex* pindexNext, const CDeterministicMNList& curMNList, CDeterministicMNList& newMNList); + bool MigrateDBIfNeeded(); void DoMaintenance(); diff --git a/src/evo/evodb.h b/src/evo/evodb.h index 5758fe36d976..9334603f24f3 100644 --- a/src/evo/evodb.h +++ b/src/evo/evodb.h @@ -11,7 +11,8 @@ // "b_b" was used in the initial version of deterministic MN storage // "b_b2" was used after compact diffs were introduced -static const std::string EVODB_BEST_BLOCK = "b_b2"; +// "b_b3" was used after masternode type introduction in evoDB +static const std::string EVODB_BEST_BLOCK = "b_b3"; class CEvoDB; diff --git a/src/init.cpp b/src/init.cpp index 818ac65f1cc3..f38216ee061d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2161,6 +2161,11 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc break; // out of the chainstate activation do-while } + if (!deterministicMNManager->MigrateDBIfNeeded()) { + strLoadError = _("Error upgrading evo database"); + break; + } + if (!llmq::quorumBlockProcessor->UpgradeDB()) { strLoadError = _("Error upgrading evo database"); break; From 00d8e8f55578b6634ea39a683f64a9bad2c5b452 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 21 Dec 2022 20:22:51 +0200 Subject: [PATCH 07/64] linter fixes --- test/functional/feature_llmq_hpmn.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index 4a4a5ecfa933..709f939894ec 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -9,16 +9,9 @@ Checks LLMQs Quorum Rotation ''' -from io import BytesIO - from test_framework.test_framework import DashTestFramework -from test_framework.messages import CBlock, CBlockHeader, CCbTx, CMerkleBlock, FromHex, hash256, msg_getmnlistd, \ - QuorumId -from test_framework.mininode import P2PInterface from test_framework.util import ( - assert_equal, - assert_greater_than_or_equal, - wait_until, + assert_equal ) From d60a7172dcb2300397d284598a5f633af649da1a Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 21 Dec 2022 20:29:16 +0200 Subject: [PATCH 08/64] adapted rpc_platform_filter for platform llmqtype --- test/functional/rpc_platform_filter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/rpc_platform_filter.py b/test/functional/rpc_platform_filter.py index 82c45be46f87..5dc0437fffe0 100755 --- a/test/functional/rpc_platform_filter.py +++ b/test/functional/rpc_platform_filter.py @@ -87,8 +87,8 @@ def test_command(method, params, auth, expexted_status, should_not_match=False): test_command("getblockhash", [0], rpcuser_authpair_platform, 200) test_command("getblockcount", [], rpcuser_authpair_platform, 200) test_command("getbestchainlock", [], rpcuser_authpair_platform, 500) - test_command("quorum", ["sign", 100], rpcuser_authpair_platform, 500) - test_command("quorum", ["sign", 100, "0000000000000000000000000000000000000000000000000000000000000000", + test_command("quorum", ["sign", 106], rpcuser_authpair_platform, 500) + test_command("quorum", ["sign", 106, "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000001"], rpcuser_authpair_platform, 200) test_command("quorum", ["verify"], rpcuser_authpair_platform, 500) From 8cdb7a0d1bc5bb32d5c0933dc57339fb32295e63 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 22 Dec 2022 15:34:09 +0200 Subject: [PATCH 09/64] Merge regular and highperf protx rpc code --- src/rpc/evo.cpp | 200 ++++++------------------------------------------ 1 file changed, 24 insertions(+), 176 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index faf611bc3c8c..487115addde9 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -321,9 +321,11 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const CMu return sendrawtransaction(sendRequest).get_str(); } -static void protx_register_fund_help(const JSONRPCRequest& request) +static void protx_register_fund_help(const JSONRPCRequest& request, const bool hpmn = false) { - RPCHelpMan{"protx register_fund", + std::string rpc_example = hpmn ? "\"register_fund_highperf" : "\"register_fund"; + rpc_example.append(" \\\"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\\\" \\\"1.2.3.4:1234\\\" \\\"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\\\" \\\"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\\\" \\\"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\\\" 0 \\\"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\\\"\""); + RPCHelpMan{hpmn ? "protx register_fund_highperf" : "protx register_fund", "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 1000 Dash\n" "to the address specified by collateralAddress and will then function as the collateral of your\n" "masternode.\n" @@ -348,14 +350,16 @@ static void protx_register_fund_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", rpc_example) }, }.Check(request); } -static void protx_register_help(const JSONRPCRequest& request) +static void protx_register_help(const JSONRPCRequest& request, const bool hpmn = false) { - RPCHelpMan{"protx register", + std::string rpc_example = hpmn ? "\"register_highperf" : "\"register"; + rpc_example.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\""); + RPCHelpMan{hpmn ? "protx register_highperf" : "protx register", "\nSame as \"protx register_fund\", but with an externally referenced collateral.\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" @@ -379,13 +383,15 @@ static void protx_register_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", "register \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", rpc_example) }, }.Check(request); } -static void protx_register_prepare_help(const JSONRPCRequest& request) +static void protx_register_prepare_help(const JSONRPCRequest& request, const bool hpmn = false) { + std::string rpc_example = hpmn ? "\"register_prepare_highperf" : "\"register_prepare"; + rpc_example.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\""); RPCHelpMan{"protx register_prepare", "\nCreates an unsigned ProTx and a message that must be signed externally\n" "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" @@ -409,7 +415,7 @@ static void protx_register_prepare_help(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, }}, RPCExamples{ - HelpExampleCli("protx", "register_prepare \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", rpc_example) }, }.Check(request); } @@ -433,18 +439,20 @@ static void protx_register_submit_help(const JSONRPCRequest& request) }, }.Check(request); } + static UniValue protx_register_wrapper(const JSONRPCRequest& request, const bool specific_legacy_bls_scheme, + const bool isHPMN, const bool isExternalRegister, const bool isFundRegister, const bool isPrepareRegister) { if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request); + protx_register_fund_help(request, isHPMN); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request); + protx_register_help(request, isHPMN); } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request); + protx_register_prepare_help(request, isHPMN); } std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); @@ -456,9 +464,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, size_t paramIdx = 0; - //TODO: Parse request and adapt accordingly - bool high_performance_masternode{false}; - CAmount collateralAmount = high_performance_masternode ? 4000 * COIN : 1000 * COIN; + CAmount collateralAmount = isHPMN ? 4000 * COIN : 1000 * COIN; CMutableTransaction tx; tx.nVersion = 3; @@ -469,7 +475,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; else ptx.nVersion = CProRegTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); - ptx.nType = high_performance_masternode ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; + ptx.nType = isHPMN ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); @@ -609,7 +615,7 @@ static UniValue protx_register(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister"; bool isFundRegister = request.strMethod == "protxregister_fund"; bool isPrepareRegister = request.strMethod == "protxregister_prepare"; - return protx_register_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_wrapper(request, false, false, isExternalRegister, isFundRegister, isPrepareRegister); } static UniValue protx_register_legacy(const JSONRPCRequest& request) @@ -617,7 +623,7 @@ static UniValue protx_register_legacy(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister_legacy"; bool isFundRegister = request.strMethod == "protxregister_fund_legacy"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_legacy"; - return protx_register_wrapper(request, true, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_wrapper(request, true, false, isExternalRegister, isFundRegister, isPrepareRegister); } // handles register, register_prepare and register_fund in one method @@ -626,165 +632,7 @@ static UniValue protx_register_highperf(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister_highperf"; bool isFundRegister = request.strMethod == "protxregister_fund_highperf"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_highperf"; - - if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request); - } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request); - } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request); - } - - std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); - if (!wallet) return NullUniValue; - - if (isExternalRegister || isFundRegister) { - EnsureWalletIsUnlocked(wallet.get()); - } - - size_t paramIdx = 0; - - CAmount collateralAmount = 4000 * COIN; - - CMutableTransaction tx; - tx.nVersion = 3; - tx.nType = TRANSACTION_PROVIDER_REGISTER; - - CProRegTx ptx; - ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; - ptx.nVersion = CProRegTx::CURRENT_VERSION; - - if (isFundRegister) { - CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); - if (!IsValidDestination(collateralDest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); - } - CScript collateralScript = GetScriptForDestination(collateralDest); - - CTxOut collateralTxOut(collateralAmount, collateralScript); - tx.vout.emplace_back(collateralTxOut); - - paramIdx++; - } else { - uint256 collateralHash = ParseHashV(request.params[paramIdx], "collateralHash"); - int32_t collateralIndex = ParseInt32V(request.params[paramIdx + 1], "collateralIndex"); - if (collateralHash.IsNull() || collateralIndex < 0) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex)); - } - - ptx.collateralOutpoint = COutPoint(collateralHash, (uint32_t)collateralIndex); - paramIdx += 2; - - // TODO unlock on failure - LOCK(wallet->cs_wallet); - wallet->LockCoin(ptx.collateralOutpoint); - } - - if (request.params[paramIdx].get_str() != "") { - if (!Lookup(request.params[paramIdx].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { - throw std::runtime_error(strprintf("invalid network address %s", request.params[paramIdx].get_str())); - } - } - - ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address"); - CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address"); - CKeyID keyIDVoting = ptx.keyIDOwner; - - if (request.params[paramIdx + 3].get_str() != "") { - keyIDVoting = ParsePubKeyIDFromAddress(request.params[paramIdx + 3].get_str(), "voting address"); - } - - int64_t operatorReward; - if (!ParseFixedPoint(request.params[paramIdx + 4].getValStr(), 2, &operatorReward)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be a number"); - } - if (operatorReward < 0 || operatorReward > 10000) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be between 0.00 and 100.00"); - } - ptx.nOperatorReward = operatorReward; - - CTxDestination payoutDest = DecodeDestination(request.params[paramIdx + 5].get_str()); - if (!IsValidDestination(payoutDest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); - } - - ptx.pubKeyOperator = pubKeyOperator; - ptx.keyIDVoting = keyIDVoting; - ptx.scriptPayout = GetScriptForDestination(payoutDest); - - if (!isFundRegister) { - // make sure fee calculation works - ptx.vchSig.resize(65); - } - - CTxDestination fundDest = payoutDest; - if (!request.params[paramIdx + 6].isNull()) { - fundDest = DecodeDestination(request.params[paramIdx + 6].get_str()); - if (!IsValidDestination(fundDest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 6].get_str()); - } - - FundSpecialTx(wallet.get(), tx, ptx, fundDest); - UpdateSpecialTxInputsHash(tx, ptx); - - bool fSubmit{true}; - if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 7].isNull()) { - fSubmit = ParseBoolV(request.params[paramIdx + 7], "submit"); - } - - if (isFundRegister) { - uint32_t collateralIndex = (uint32_t) -1; - for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == collateralAmount) { - collateralIndex = i; - break; - } - } - CHECK_NONFATAL(collateralIndex != (uint32_t) -1); - ptx.collateralOutpoint.n = collateralIndex; - - SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx, fSubmit); - } else { - // referencing external collateral - - Coin coin; - if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort())); - } - CTxDestination txDest; - ExtractDestination(coin.out.scriptPubKey, txDest); - const CKeyID *keyID = std::get_if(&txDest); - if (!keyID) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); - } - - if (isPrepareRegister) { - // external signing with collateral key - ptx.vchSig.clear(); - SetTxPayload(tx, ptx); - - UniValue ret(UniValue::VOBJ); - ret.pushKV("tx", EncodeHexTx(CTransaction(tx))); - ret.pushKV("collateralAddress", EncodeDestination(txDest)); - ret.pushKV("signMessage", ptx.MakeSignString()); - return ret; - } else { - // lets prove we own the collateral - LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan(); - if (!spk_man) { - throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command"); - } - - CKey key; - if (!spk_man->GetKey(*keyID, key)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest))); - } - SignSpecialTxPayloadByString(tx, ptx, key); - SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx, fSubmit); - } - } + return protx_register_wrapper(request, true, false, isExternalRegister, isFundRegister, isPrepareRegister); } static UniValue protx_register_submit(const JSONRPCRequest& request) From ac17ee7c5ef2c46f909b5245620e5b72040685a1 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 28 Dec 2022 14:02:33 +0200 Subject: [PATCH 10/64] cleanup --- src/evo/deterministicmns.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index e77ea4288d8c..b812614a1205 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -50,7 +50,6 @@ void CDeterministicMN::ToJson(UniValue& obj) const pdmnState->ToJson(stateObj); obj.pushKV("type", nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); - obj.pushKV("type", nType); obj.pushKV("proTxHash", proTxHash.ToString()); obj.pushKV("collateralHash", collateralOutpoint.hash.ToString()); obj.pushKV("collateralIndex", (int)collateralOutpoint.n); From 3643450edd8ba12d3358a89ceaf27b964dd69cd5 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 28 Dec 2022 14:02:54 +0200 Subject: [PATCH 11/64] weighted governance vote --- src/governance/object.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index ee11c42c9759..d564d22c9b61 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -622,11 +622,18 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote LOCK(cs); int nCount = 0; + const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[governance->nCachedBlockHeight]); + LOCK(deterministicMNManager->cs); + CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(pindex); for (const auto& votepair : mapCurrentMNVotes) { const vote_rec_t& recVote = votepair.second; auto it2 = recVote.mapInstances.find(eVoteSignalIn); if (it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { - ++nCount; + auto mn = mnList.GetMNByCollateral(votepair.first); + if (mn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + nCount += 4; + else + ++nCount; } } return nCount; From fa5ec81395c0f49d957fabed41e1385a73f0c7d5 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 4 Jan 2023 18:39:56 +0200 Subject: [PATCH 12/64] Revert "Added v20 HF" This reverts commit 7207b3129304b82bbbf6a4b5c02a53a6325206cd. --- src/chainparams.cpp | 33 --------------------------------- src/consensus/params.h | 3 +-- src/llmq/utils.cpp | 8 -------- src/llmq/utils.h | 1 - src/versionbitsinfo.cpp | 5 ----- 5 files changed, 1 insertion(+), 49 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index aaaf2c8234fb..2bf5295f4236 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -255,15 +255,6 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 2420; // 60% of 4032 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 19999999999; // TODO: To be determined later - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 4032; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 3226; // 80% of 4032 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 2420; // 60% of 4032 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods - - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000007d31aeabf4a72ce1fbaa"); // 1796500 @@ -500,14 +491,6 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 60; // 60% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 19999999999; // TODO: To be determined later - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 80; // 80% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000002d68c04d48c0c9c"); // 808000 @@ -716,14 +699,6 @@ class CDevNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 60; // 60% of 100 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 1661990400; // Sep 1st, 2022 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 100; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 80; // 80% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 60; // 60% of 100 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000"); @@ -974,14 +949,6 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_V19].nThresholdMin = 180; // 60% of 300 consensus.vDeployments[Consensus::DEPLOYMENT_V19].nFalloffCoeff = 5; // this corresponds to 10 periods - consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 0; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nTimeout = 999999999999ULL; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nWindowSize = 300; - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdStart = 240; // 80% of 300 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nThresholdMin = 180; // 60% of 300 - consensus.vDeployments[Consensus::DEPLOYMENT_V20].nFalloffCoeff = 5; // this corresponds to 10 periods - // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h index df0a2501b441..b538c7726c88 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -23,8 +23,7 @@ enum DeploymentPos { DEPLOYMENT_REALLOC, // Deployment of Block Reward Reallocation DEPLOYMENT_DIP0020, // Deployment of DIP0020, DIP0021 and LMQ_100_67 quorums DEPLOYMENT_DIP0024, // Deployment of DIP0024 (Quorum Rotation) and decreased governance proposal fee - DEPLOYMENT_V19, // Deployment of Basic BLS, AssetLocks - DEPLOYMENT_V20, // Deployment of 4K HPMN, EHF + DEPLOYMENT_V19, // Deployment of Basic BLS, AssetLocks, EHF // NOTE: Also add new deployments to VersionBitsDeploymentInfo in versionbits.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 1ee1d996ec6f..794ddafdf0e4 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -662,14 +662,6 @@ const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex) return pindex->GetAncestor(nHeight); } -bool IsV20Active(const CBlockIndex* pindex) -{ - assert(pindex); - - LOCK(cs_llmq_vbc); - return VersionBitsState(pindex, Params().GetConsensus(), Consensus::DEPLOYMENT_V20, llmq_versionbitscache) == ThresholdState::ACTIVE; -} - bool IsInstantSendLLMQTypeShared() { if (Params().GetConsensus().llmqTypeInstantSend == Params().GetConsensus().llmqTypeChainLocks || diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 7ea1db0defe2..6088dd6bbbd8 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -85,7 +85,6 @@ Consensus::LLMQType GetInstantSendLLMQType(bool deterministic); bool IsDIP0024Active(const CBlockIndex* pindex); bool IsV19Active(const CBlockIndex* pindex); const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex); -bool IsV20Active(const CBlockIndex* pindex); static bool IsInstantSendLLMQTypeShared(); static bool IsPlatformLLMQType(Consensus::LLMQType llmqType); diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp index 4f57ab5ff5b6..fb94fa7e3962 100644 --- a/src/versionbitsinfo.cpp +++ b/src/versionbitsinfo.cpp @@ -56,10 +56,5 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/"v19", /*.gbt_force =*/true, /*.check_mn_protocol =*/false, - }, - { - /*.name =*/"v20", - /*.gbt_force =*/true, - /*.check_mn_protocol =*/false, } }; From 4f4698e22717824733659eaa83185ecd2d9f98fd Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 4 Jan 2023 18:55:17 +0200 Subject: [PATCH 13/64] Switched to v19 --- src/evo/deterministicmns.cpp | 4 ++++ src/governance/object.cpp | 3 ++- src/rpc/evo.cpp | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index b812614a1205..0e3c78bdd0f2 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -700,6 +700,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } + if (proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && !llmq::utils::IsV19Active(pindexPrev)) { + return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); + } + auto dmn = std::make_shared(newList.GetTotalRegisteredCount(), proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE); dmn->proTxHash = tx.GetHash(); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index d564d22c9b61..46217766fa84 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -623,6 +623,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote int nCount = 0; const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[governance->nCachedBlockHeight]); + bool isV19Active = llmq::utils::IsV19Active(pindex); LOCK(deterministicMNManager->cs); CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(pindex); for (const auto& votepair : mapCurrentMNVotes) { @@ -630,7 +631,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote auto it2 = recVote.mapInstances.find(eVoteSignalIn); if (it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { auto mn = mnList.GetMNByCollateral(votepair.first); - if (mn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + if (mn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && isV19Active) nCount += 4; else ++nCount; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 487115addde9..52e20ec2d607 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -462,6 +462,11 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, EnsureWalletIsUnlocked(wallet.get()); } + bool isV19active = !llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); + if (isHPMN && !isV19active) { + throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); + } + size_t paramIdx = 0; CAmount collateralAmount = isHPMN ? 4000 * COIN : 1000 * COIN; From df2fdc2aed5e7c86baa4c7ce9951e056e938dc58 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 4 Jan 2023 19:02:49 +0200 Subject: [PATCH 14/64] Added type in json --- src/evo/providertx.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evo/providertx.h b/src/evo/providertx.h index b6cb4986b1de..7c2e3933c8d9 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -84,6 +84,7 @@ class CProRegTx obj.clear(); obj.setObject(); obj.pushKV("version", nVersion); + obj.pushKV("type", nType); obj.pushKV("collateralHash", collateralOutpoint.hash.ToString()); obj.pushKV("collateralIndex", (int)collateralOutpoint.n); obj.pushKV("service", addr.ToString(false)); From 15bfa615bc4e32af18819c30b74ab33275daade5 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 11 Jan 2023 17:53:00 +0200 Subject: [PATCH 15/64] HPMN payee impl + various fixes --- src/evo/deterministicmns.cpp | 32 +++++- src/evo/dmnstate.cpp | 1 + src/evo/dmnstate.h | 11 +- src/rpc/evo.cpp | 2 +- src/rpc/masternode.cpp | 1 + test/functional/feature_llmq_hpmn.py | 105 +++++++++++++++++- .../test_framework/test_framework.py | 4 +- 7 files changed, 147 insertions(+), 9 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 0e3c78bdd0f2..883a1eb9311c 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -182,7 +182,27 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const return nullptr; } - CDeterministicMNCPtr best; + // Starting from v19 and until v20 (Platform release), HPMN will be rewarded 4 blocks in a row + // No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation + // TODO: Skip this code once v20 is active + CDeterministicMNCPtr best = nullptr; + ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->nLastPaidHeight == nHeight) { + // We found the last MN Payee. + // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if nConsecutivePayments < 3 + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->pdmnState->nConsecutivePayments < 3) { + best = dmn; + } + } + return; + }); + + if (best) + return best; + + // Note: If the last payee was a regular MN or if the payee is a HPMN that was removed from the mnList then that's fine. + // We can proceed with classic MN payee selection + ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { if (!best || CompareByLastPaid(dmn.get(), best.get())) { best = dmn; @@ -881,6 +901,16 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C if (payee && newList.HasMN(payee->proTxHash)) { auto newState = std::make_shared(*newList.GetMN(payee->proTxHash)->pdmnState); newState->nLastPaidHeight = nHeight; + // Starting from v19 and until v20, HPMN will be paid 4 blocks in a row + // No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation + // TODO: Skip this code once v20 is active + // Note: If the payee wasn't found in the current block that's fine + if (payee->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (newState->nConsecutivePayments == 3) + newState->nConsecutivePayments = 0; + else + newState->nConsecutivePayments++; + } newList.UpdateMN(payee->proTxHash, newState); } diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index c793fed55ded..267c3148da59 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -39,6 +39,7 @@ void CDeterministicMNState::ToJson(UniValue& obj) const obj.pushKV("service", addr.ToStringIPPort(false)); obj.pushKV("registeredHeight", nRegisteredHeight); obj.pushKV("lastPaidHeight", nLastPaidHeight); + obj.pushKV("consecutivePayments", nConsecutivePayments); obj.pushKV("PoSePenalty", nPoSePenalty); obj.pushKV("PoSeRevivedHeight", nPoSeRevivedHeight); obj.pushKV("PoSeBanHeight", nPoSeBanHeight); diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index 634ca2f5aa2c..adaa4d397a1a 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -35,6 +35,7 @@ class CDeterministicMNState public: int nRegisteredHeight{-1}; int nLastPaidHeight{0}; + int nConsecutivePayments{0}; int nPoSePenalty{0}; int nPoSeRevivedHeight{-1}; uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED}; @@ -73,6 +74,7 @@ class CDeterministicMNState READWRITE( obj.nRegisteredHeight, obj.nLastPaidHeight, + obj.nConsecutivePayments, obj.nPoSePenalty, obj.nPoSeRevivedHeight, obj.nPoSeBanHeight, @@ -147,11 +149,12 @@ class CDeterministicMNStateDiff Field_addr = 0x0800, Field_scriptPayout = 0x1000, Field_scriptOperatorPayout = 0x2000, + Field_nConsecutivePayments = 0x4000, }; -#define DMN_STATE_DIFF_ALL_FIELDS \ +#define DMN_STATE_DIFF_ALL_FIELDS_BUT_CONSECUTIVE_PAYMENTS \ DMN_STATE_DIFF_LINE(nRegisteredHeight) \ - DMN_STATE_DIFF_LINE(nLastPaidHeight) \ + DMN_STATE_DIFF_LINE(nLastPaidHeight) \ DMN_STATE_DIFF_LINE(nPoSePenalty) \ DMN_STATE_DIFF_LINE(nPoSeRevivedHeight) \ DMN_STATE_DIFF_LINE(nPoSeBanHeight) \ @@ -165,6 +168,10 @@ class CDeterministicMNStateDiff DMN_STATE_DIFF_LINE(scriptPayout) \ DMN_STATE_DIFF_LINE(scriptOperatorPayout) +#define DMN_STATE_DIFF_ALL_FIELDS \ + DMN_STATE_DIFF_ALL_FIELDS_BUT_CONSECUTIVE_PAYMENTS \ + DMN_STATE_DIFF_LINE(nConsecutivePayments) \ + public: uint32_t fields{0}; // we reuse the state class, but only the members as noted by fields are valid diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 52e20ec2d607..3c0c81fb57b6 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -637,7 +637,7 @@ static UniValue protx_register_highperf(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister_highperf"; bool isFundRegister = request.strMethod == "protxregister_fund_highperf"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_highperf"; - return protx_register_wrapper(request, true, false, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_wrapper(request, false, true, isExternalRegister, isFundRegister, isPrepareRegister); } static UniValue protx_register_submit(const JSONRPCRequest& request) diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 9d58cdde289f..a3213a9cbaaa 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -668,6 +668,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("status", dmnToStatus(dmn)); objMN.pushKV("type", dmn.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty); + objMN.pushKV("nConsecutivePayments", dmn.pdmnState->nConsecutivePayments); objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn)); objMN.pushKV("lastpaidblock", dmn.pdmnState->nLastPaidHeight); objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner))); diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index 709f939894ec..4e3ea638584a 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -9,9 +9,14 @@ Checks LLMQs Quorum Rotation ''' +from collections import defaultdict +from decimal import Decimal +import json +import time + from test_framework.test_framework import DashTestFramework from test_framework.util import ( - assert_equal + assert_equal, force_finish_mnsync, p2p_port ) @@ -23,12 +28,16 @@ def intersection(lst1, lst2): def extract_quorum_members(quorum_info): return [d['proTxHash'] for d in quorum_info["members"]] +class HPMN(object): + pass class LLMQHPMNTest(DashTestFramework): def set_test_params(self): - self.set_dash_test_params(9, 8, fast_dip3_enforcement=True, hpmn_count=4) + self.set_dash_test_params(9, 4, fast_dip3_enforcement=True, hpmn_count=4) self.set_dash_llmq_test_params(4, 4) + #self.supports_cli = False + def run_test(self): llmq_type = 106 llmq_type_name = "llmq_test_platform" @@ -46,6 +55,13 @@ def run_test(self): self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", 0) self.wait_for_sporks_same() + self.activate_v19(expected_activation_height=900) + self.log.info("Activated v19 at height:" + str(self.nodes[0].getblockcount())) + + self.log.info("Test HPMN payments") + self.test_hpmmn_payements(window_analysis=48) + + self.log.info("Test llmq_platform are formed only with HPMN") quorum_0_hash = self.mine_quorum(llmq_type_name, llmq_type) self.test_quorum_members_are_high_performance(llmq_type, quorum_0_hash) @@ -55,8 +71,93 @@ def run_test(self): quorum_2_hash = self.mine_quorum(llmq_type_name, llmq_type) self.test_quorum_members_are_high_performance(llmq_type, quorum_2_hash) + + return + def prepare_hpmn(self, node, idx, alias): + hpmn = HPMN() + hpmn.idx = idx + hpmn.alias = alias + hpmn.p2p_port = p2p_port(hpmn.idx) + + address = node.getnewaddress() + blsKey = node.bls('generate') + hpmn.fundsAddr = address + hpmn.ownerAddr = address + hpmn.operatorAddr = blsKey['public'] + hpmn.votingAddr = address + hpmn.blsMnkey = blsKey['secret'] + + return hpmn + + def register_fund_mn(self, node, mn): + node.sendtoaddress(mn.fundsAddr, 1000.001) + mn.collateral_address = node.getnewaddress() + mn.rewards_address = node.getnewaddress() + + mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) + mn.collateral_txid = mn.protx_hash + mn.collateral_vout = None + + rawtx = node.getrawtransaction(mn.collateral_txid, 1) + for txout in rawtx['vout']: + if txout['value'] == Decimal(1000): + mn.collateral_vout = txout['n'] + break + assert mn.collateral_vout is not None + + self.log.info(">"+str(node.getrawtransaction(mn.protx_hash, 1))) + + def start_mn(self, mn): + self.log.info("len(self.nodes) = " + str(len(self.nodes)) + " mn.idx = " + str(mn.idx)) + if len(self.nodes) <= mn.idx: + self.log.info("len(self.nodes) = " + str(len(self.nodes)) + " mn.idx = " + str(mn.idx)) + self.add_nodes(mn.idx - len(self.nodes) + 1) + # assert len(self.nodes) == mn.idx + 1 + #self.start_node(mn.idx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.blsMnkey]) + #force_finish_mnsync(self.nodes[mn.idx]) + #mn.node = self.nodes[mn.idx] + #self.connect_nodes(mn.idx, 0) + #self.sync_all() + + def test_hpmmn_payements(self, window_analysis): + mn_payees = list() + + for i in range(0, window_analysis): + payee_info = self.get_mn_payee_for_block(self.nodes[0].getbestblockhash()) + mn_payees.append(payee_info) + + self.nodes[0].generate(1) + self.sync_blocks() + + verified_hpmn = None + for i in range(len(mn_payees)): + # Start checking from the first payee different from the first element of the window analysis + if i > 0 and mn_payees[i] != mn_payees[0]: + # Check only HPMN + if mn_payees[i].hpmn: + # Skip already checked payee + if mn_payees[i].proTxHash == verified_hpmn: + continue + # Verify that current HPMN is payed for 4 blocks in a row + for j in range(1, 4): + # Avoid overflow check + if (i + j) < len(mn_payees): + assert_equal(mn_payees[i].proTxHash, mn_payees[i+j].proTxHash) + verified_hpmn = mn_payees[i].proTxHash + + def get_mn_payee_for_block(self, block_hash): + + mn_payee_info = self.nodes[0].masternode("payments", block_hash)[0] + mn_payee_protx = mn_payee_info['masternodes'][0]['proTxHash'] + + mninfos_online = self.mninfo.copy() + for mn_info in mninfos_online: + if mn_info.proTxHash == mn_payee_protx: + return mn_info + return None + def test_quorum_members_are_high_performance(self, llmq_type, quorum_hash): quorum_info = self.nodes[0].quorum("info", llmq_type, quorum_hash) quorum_members = extract_quorum_members(quorum_info) diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index d01e824c674f..93d7314aeb21 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -960,9 +960,7 @@ def prepare_masternodes(self): self.log.info("Preparing %d masternodes" % self.mn_count) rewardsAddr = self.nodes[0].getnewaddress() - for idx in range(0, self.hpmn_count): - self.prepare_masternode(idx, rewardsAddr, True) - for idx in range(self.hpmn_count, self.mn_count): + for idx in range(0, self.mn_count): self.prepare_masternode(idx, rewardsAddr, False) self.sync_all() From b966568ca028f17753aab2ced735bf4393b0f5b2 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 11 Jan 2023 18:49:33 +0200 Subject: [PATCH 16/64] Added some comments --- src/evo/deterministicmns.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 02df3d8272c7..9294dd014332 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -471,6 +471,8 @@ class CDeterministicMNListDiff tmp = ReadCompactSize(s); for (size_t i = 0; i < tmp; i++) { CDeterministicMN mn(0); + // Unserialise CDeterministicMN using CURRENT_MN_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE + // It will be later written with format MN_TYPE_FORMAT which includes the type field. mn.Unserialize(s, format_version); auto dmn = std::make_shared(mn); addedMNs.push_back(dmn); @@ -478,6 +480,9 @@ class CDeterministicMNListDiff tmp = ReadCompactSize(s); for (size_t i = 0; i < tmp; i++) { CDeterministicMNStateDiff diff; + // CDeterministicMNState holds a new field (nConsecutivePayments) but no migration is needed here since: + // CDeterministicMNStateDiff is always serialised using a bitmask. + // Because the new field (nConsecutivePayments) has a new bit guide value then we are good to continue tmp2 = ReadVarInt(s); s >> diff; updatedMNs.emplace(tmp2, std::move(diff)); From 5ff08a064f7285e136472d0eb242e496b287ab06 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Fri, 13 Jan 2023 14:52:59 +0200 Subject: [PATCH 17/64] Cleaner governance weight vote --- src/governance/object.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 46217766fa84..ec4a4a679fd5 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -622,19 +622,20 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote LOCK(cs); int nCount = 0; - const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[governance->nCachedBlockHeight]); - bool isV19Active = llmq::utils::IsV19Active(pindex); - LOCK(deterministicMNManager->cs); - CDeterministicMNList mnList = deterministicMNManager->GetListForBlock(pindex); for (const auto& votepair : mapCurrentMNVotes) { const vote_rec_t& recVote = votepair.second; auto it2 = recVote.mapInstances.find(eVoteSignalIn); if (it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { - auto mn = mnList.GetMNByCollateral(votepair.first); - if (mn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && isV19Active) - nCount += 4; - else - ++nCount; + Coin coin; + int voteWeight = 1; + if (!GetUTXOCoin(votepair.first, coin)) { + // 4x times weight vote for HPMN owners. + // No need to check if v19 is active since no HPMN are allowed to register before v19 + if (coin.out.nValue == 4000) { + voteWeight = 4; + } + } + nCount += voteWeight; } } return nCount; From 1e4c5f1845e8f79a1b62cc483e6b784a78d174c1 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Fri, 13 Jan 2023 16:25:48 +0200 Subject: [PATCH 18/64] protocol version bump for CDeterministicMN serialisation --- src/evo/deterministicmns.h | 8 ++++++-- src/version.h | 5 ++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 9294dd014332..b7ee896d2d17 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -74,8 +74,12 @@ class CDeterministicMN READWRITE(collateralOutpoint); READWRITE(nOperatorReward); READWRITE(pdmnState); - //TODO: Include check for protocol version, so it won't break clients - if (format_version >= MN_TYPE_FORMAT) { + // We need to serialise nType if: + // format_version is set to MN_TYPE_FORMAT (For Serialisation it is always the case) Needed for the MNLISTDIFF Migration in evoDB + // We can't know if we are serialising for the Disk or for the Network here (s.GetType() is not accessible) + // Therefore if s.GetVersion() == CLIENT_VERSION -> Then we know we are serialising for the Disk + // Otherwise, we can safely check with protocol versioning logic so we won't break old clients + if (format_version >= MN_TYPE_FORMAT && (s.GetVersion() == CLIENT_VERSION || s.GetVersion() >= DMN_TYPE_PROTO_VERSION)) { READWRITE(nType); } else { diff --git a/src/version.h b/src/version.h index 1ded6e1ba363..456b44d78423 100644 --- a/src/version.h +++ b/src/version.h @@ -11,7 +11,7 @@ */ -static const int PROTOCOL_VERSION = 70226; +static const int PROTOCOL_VERSION = 70227; //! initial proto version, to be increased after version/verack negotiation static const int INIT_PROTO_VERSION = 209; @@ -50,6 +50,9 @@ static const int BLS_SCHEME_PROTO_VERSION = 70225; //! DSQ and DSTX started using protx hash in this version static const int COINJOIN_PROTX_HASH_PROTO_VERSION = 70226; +//! Masternode type was introduced in this version +static const int DMN_TYPE_PROTO_VERSION = 70227; + // Make sure that none of the values above collide with `ADDRV2_FORMAT`. #endif // BITCOIN_VERSION_H From 22e721cea210bb1dd78a99d2381ed47683cad8cc Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sat, 14 Jan 2023 16:39:47 +0200 Subject: [PATCH 19/64] Removed highperf protx. Moved logic inside protx --- src/rpc/evo.cpp | 96 ++++++++++++++++++++++++++----------------------- 1 file changed, 52 insertions(+), 44 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 3c0c81fb57b6..48398f9b4f30 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -47,6 +47,10 @@ static RPCArg GetRpcArg(const std::string& strParamName) {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The dash address to send the collateral to."} }, + {"collateralAmount", + {"collateralAmount", RPCArg::Type::NUM, RPCArg::Optional::NO, + "The collateral amount. Possible values 1000 (MN) or 4000 (HPMN)."} + }, {"collateralHash", {"collateralHash", RPCArg::Type::STR, RPCArg::Optional::NO, "The collateral transaction hash."} @@ -321,19 +325,19 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const CMu return sendrawtransaction(sendRequest).get_str(); } -static void protx_register_fund_help(const JSONRPCRequest& request, const bool hpmn = false) +static void protx_register_fund_help(const JSONRPCRequest& request) { - std::string rpc_example = hpmn ? "\"register_fund_highperf" : "\"register_fund"; - rpc_example.append(" \\\"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\\\" \\\"1.2.3.4:1234\\\" \\\"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\\\" \\\"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\\\" \\\"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\\\" 0 \\\"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\\\"\""); - RPCHelpMan{hpmn ? "protx register_fund_highperf" : "protx register_fund", - "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 1000 Dash\n" + RPCHelpMan{"protx register_fund", + "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move requested collateralAmount Dash\n" "to the address specified by collateralAddress and will then function as the collateral of your\n" "masternode.\n" + "If the collaretalAmount is set to 4000 it will lead to a HPMN fund registration. Otherwise, 1000 will be required for a regular MN." "A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n" "is fully deployed.\n" + HELP_REQUIRING_PASSPHRASE, { GetRpcArg("collateralAddress"), + GetRpcArg("collateralAmount"), GetRpcArg("ipAndPort"), GetRpcArg("ownerAddress"), GetRpcArg("operatorPubKey_register"), @@ -350,18 +354,17 @@ static void protx_register_fund_help(const JSONRPCRequest& request, const bool h RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", rpc_example) + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" 1000 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } -static void protx_register_help(const JSONRPCRequest& request, const bool hpmn = false) +static void protx_register_help(const JSONRPCRequest& request) { - std::string rpc_example = hpmn ? "\"register_highperf" : "\"register"; - rpc_example.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\""); - RPCHelpMan{hpmn ? "protx register_highperf" : "protx register", + RPCHelpMan{"protx register", "\nSame as \"protx register_fund\", but with an externally referenced collateral.\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" + "If the unspent collateral are 4000 DASH this will lead to a HPMN registration. Otherwise, 1000 for a usual masternode registration" "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" + HELP_REQUIRING_PASSPHRASE, { @@ -383,18 +386,17 @@ static void protx_register_help(const JSONRPCRequest& request, const bool hpmn = RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", rpc_example) + HelpExampleCli("protx", "register \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } -static void protx_register_prepare_help(const JSONRPCRequest& request, const bool hpmn = false) +static void protx_register_prepare_help(const JSONRPCRequest& request) { - std::string rpc_example = hpmn ? "\"register_prepare_highperf" : "\"register_prepare"; - rpc_example.append(" \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\""); RPCHelpMan{"protx register_prepare", "\nCreates an unsigned ProTx and a message that must be signed externally\n" "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" + "If the unspent collateral are 4000 DASH this will lead to a HPMN registration. Otherwise, 1000 for a usual masternode registration" "The prepared transaction will also contain inputs and outputs to cover fees.\n", { GetRpcArg("collateralHash"), @@ -415,7 +417,7 @@ static void protx_register_prepare_help(const JSONRPCRequest& request, const boo {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, }}, RPCExamples{ - HelpExampleCli("protx", rpc_example) + HelpExampleCli("protx", "register_prepare \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } @@ -442,17 +444,16 @@ static void protx_register_submit_help(const JSONRPCRequest& request) static UniValue protx_register_wrapper(const JSONRPCRequest& request, const bool specific_legacy_bls_scheme, - const bool isHPMN, const bool isExternalRegister, const bool isFundRegister, const bool isPrepareRegister) { - if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request, isHPMN); + if (isFundRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { + protx_register_fund_help(request); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request, isHPMN); + protx_register_help(request); } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request, isHPMN); + protx_register_prepare_help(request); } std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); @@ -462,15 +463,10 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, EnsureWalletIsUnlocked(wallet.get()); } - bool isV19active = !llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); - if (isHPMN && !isV19active) { - throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); - } + bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); size_t paramIdx = 0; - CAmount collateralAmount = isHPMN ? 4000 * COIN : 1000 * COIN; - CMutableTransaction tx; tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_REGISTER; @@ -479,17 +475,29 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, if (specific_legacy_bls_scheme) ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; else - ptx.nVersion = CProRegTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); - ptx.nType = isHPMN ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; + ptx.nVersion = CProRegTx::GetVersion(isV19active); + ptx.nType = CProRegTx::TYPE_REGULAR_MASTERNODE; //Will be eventually updated later + CAmount fundRequestedAmount = 0; //Will be updated later if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); if (!IsValidDestination(collateralDest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); } + paramIdx++; + int32_t fundRequestedCollateralAmount = ParseInt32V(request.params[paramIdx], "collateralAmount"); + if (fundRequestedCollateralAmount != 1000 && fundRequestedCollateralAmount != 4000) { + throw JSONRPCError(RPC_INVALID_REQUEST, "invalid collateralAmount: must be either 1000 or 4000."); + } + if (fundRequestedCollateralAmount == 4000) { + if (!isV19active) { + throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); + } + ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + } CScript collateralScript = GetScriptForDestination(collateralDest); - - CTxOut collateralTxOut(collateralAmount, collateralScript); + fundRequestedAmount = fundRequestedCollateralAmount * COIN; + CTxOut collateralTxOut(fundRequestedAmount, collateralScript); tx.vout.emplace_back(collateralTxOut); paramIdx++; @@ -563,7 +571,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, if (isFundRegister) { uint32_t collateralIndex = (uint32_t) -1; for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == collateralAmount) { + if (tx.vout[i].nValue == fundRequestedAmount) { collateralIndex = i; break; } @@ -571,6 +579,8 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, CHECK_NONFATAL(collateralIndex != (uint32_t) -1); ptx.collateralOutpoint.n = collateralIndex; + LogPrintf("takis fund ptx[%d] fundRequestedAmount[%d]\n", ptx.nType, fundRequestedAmount); + SetTxPayload(tx, ptx); return SignAndSendSpecialTx(request, tx, fSubmit); } else { @@ -580,6 +590,13 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort())); } + CAmount HPMNCollateralAmount = 4000 * COIN; + if (coin.out.nValue == HPMNCollateralAmount) { + if (!isV19active) { + throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); + } + ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + } CTxDestination txDest; ExtractDestination(coin.out.scriptPubKey, txDest); const PKHash *pkhash = std::get_if(&txDest); @@ -587,6 +604,8 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); } + LogPrintf("takis non-fund ptx[%d] coint_value[%d]\n", ptx.nType, coin.out.nValue); + if (isPrepareRegister) { // external signing with collateral key ptx.vchSig.clear(); @@ -620,7 +639,7 @@ static UniValue protx_register(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister"; bool isFundRegister = request.strMethod == "protxregister_fund"; bool isPrepareRegister = request.strMethod == "protxregister_prepare"; - return protx_register_wrapper(request, false, false, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister); } static UniValue protx_register_legacy(const JSONRPCRequest& request) @@ -628,16 +647,7 @@ static UniValue protx_register_legacy(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister_legacy"; bool isFundRegister = request.strMethod == "protxregister_fund_legacy"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_legacy"; - return protx_register_wrapper(request, true, false, isExternalRegister, isFundRegister, isPrepareRegister); -} - -// handles register, register_prepare and register_fund in one method -static UniValue protx_register_highperf(const JSONRPCRequest& request) -{ - bool isExternalRegister = request.strMethod == "protxregister_highperf"; - bool isFundRegister = request.strMethod == "protxregister_fund_highperf"; - bool isPrepareRegister = request.strMethod == "protxregister_prepare_highperf"; - return protx_register_wrapper(request, false, true, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_wrapper(request, true, isExternalRegister, isFundRegister, isPrepareRegister); } static UniValue protx_register_submit(const JSONRPCRequest& request) @@ -1289,8 +1299,6 @@ static UniValue protx(const JSONRPCRequest& request) return protx_register(new_request); } else if (command == "protxregister_legacy" || command == "protxregister_fund_legacy" || command == "protxregister_prepare_legacy") { return protx_register_legacy(new_request); - } else if (command == "protxregister_highperf" || command == "protxregister_fund_highperf" || command == "protxregister_prepare_highperf") { - return protx_register_highperf(new_request); } else if (command == "protxregister_submit") { return protx_register_submit(new_request); } else if (command == "protxupdate_service") { From 0ca868dbfcf1ca3fad8c9bf6953e91b00f5424b8 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sat, 14 Jan 2023 16:40:37 +0200 Subject: [PATCH 20/64] Correct collateral checking --- src/governance/object.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index ec4a4a679fd5..861bf0708a54 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -631,7 +631,8 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote if (!GetUTXOCoin(votepair.first, coin)) { // 4x times weight vote for HPMN owners. // No need to check if v19 is active since no HPMN are allowed to register before v19 - if (coin.out.nValue == 4000) { + CAmount HPMNCollateralAmount = 4000 * COIN; + if (coin.out.nValue == HPMNCollateralAmount) { voteWeight = 4; } } From 02f6760f5ebdc48888a2b1a6e6f3a1a97ba059a6 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 23 Jan 2023 16:36:06 +0200 Subject: [PATCH 21/64] functional tests refactoring --- .../feature_dip3_deterministicmns.py | 2 +- test/functional/feature_llmq_hpmn.py | 151 +++++++++-------- .../test_framework/test_framework.py | 156 +++++++++++++++++- 3 files changed, 227 insertions(+), 82 deletions(-) diff --git a/test/functional/feature_dip3_deterministicmns.py b/test/functional/feature_dip3_deterministicmns.py index bbc7bd9b9961..da7fe6a6a3dd 100755 --- a/test/functional/feature_dip3_deterministicmns.py +++ b/test/functional/feature_dip3_deterministicmns.py @@ -243,7 +243,7 @@ def register_fund_mn(self, node, mn): mn.collateral_address = node.getnewaddress() mn.rewards_address = node.getnewaddress() - mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) + mn.protx_hash = node.protx('register_fund', mn.collateral_address, 1000, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) mn.collateral_txid = mn.protx_hash mn.collateral_vout = None diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index 4e3ea638584a..86f60cdb7291 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -9,14 +9,11 @@ Checks LLMQs Quorum Rotation ''' -from collections import defaultdict -from decimal import Decimal -import json -import time +from _decimal import Decimal from test_framework.test_framework import DashTestFramework from test_framework.util import ( - assert_equal, force_finish_mnsync, p2p_port + assert_equal, p2p_port ) @@ -28,26 +25,18 @@ def intersection(lst1, lst2): def extract_quorum_members(quorum_info): return [d['proTxHash'] for d in quorum_info["members"]] -class HPMN(object): - pass - class LLMQHPMNTest(DashTestFramework): def set_test_params(self): - self.set_dash_test_params(9, 4, fast_dip3_enforcement=True, hpmn_count=4) + self.set_dash_test_params(5, 4, fast_dip3_enforcement=True, hpmn_count=7) self.set_dash_llmq_test_params(4, 4) - #self.supports_cli = False - def run_test(self): - llmq_type = 106 - llmq_type_name = "llmq_test_platform" - # Connect all nodes to node1 so that we always have the whole network connected # Otherwise only masternode connections will be established between nodes, which won't propagate TXs/blocks # Usually node0 is the one that does this, but in this test we isolate it multiple times for i in range(len(self.nodes)): - if i != 1: + if i != 0: self.connect_nodes(i, 0) self.activate_dip8() @@ -55,72 +44,43 @@ def run_test(self): self.nodes[0].sporkupdate("SPORK_17_QUORUM_DKG_ENABLED", 0) self.wait_for_sporks_same() + self.mine_quorum(llmq_type_name='llmq_test', llmq_type=100) + + self.log.info("Test that HPMN registration is rejected before v19") + self.test_hpmn_is_rejected_before_v19() + self.activate_v19(expected_activation_height=900) self.log.info("Activated v19 at height:" + str(self.nodes[0].getblockcount())) - self.log.info("Test HPMN payments") - self.test_hpmmn_payements(window_analysis=48) + self.move_to_next_cycle() + self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount())) + self.move_to_next_cycle() + self.log.info("Cycle H+C height:" + str(self.nodes[0].getblockcount())) + self.move_to_next_cycle() + self.log.info("Cycle H+2C height:" + str(self.nodes[0].getblockcount())) - self.log.info("Test llmq_platform are formed only with HPMN") - quorum_0_hash = self.mine_quorum(llmq_type_name, llmq_type) - self.test_quorum_members_are_high_performance(llmq_type, quorum_0_hash) + (quorum_info_i_0, quorum_info_i_1) = self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103) - quorum_1_hash = self.mine_quorum(llmq_type_name, llmq_type) - self.test_quorum_members_are_high_performance(llmq_type, quorum_1_hash) + hpmn_protxhash_list = list() + for i in range(6): + protx = self.dynamically_add_masternode(hpmn=True) + hpmn_protxhash_list.append(protx) + self.nodes[0].generate(8) + self.sync_blocks(self.nodes) - quorum_2_hash = self.mine_quorum(llmq_type_name, llmq_type) - self.test_quorum_members_are_high_performance(llmq_type, quorum_2_hash) + self.log.info("Test llmq_platform are formed only with HPMN") + for i in range(3): + quorum_i_hash = self.mine_quorum(llmq_type_name='llmq_test_platform', llmq_type=106) + self.test_quorum_members_are_high_performance(quorum_i_hash, llmq_type=106) + self.log.info("Test that HPMN are present in MN list") + self.test_hpmn_protx_are_in_mnlist(hpmn_protxhash_list) + self.log.info("Test that HPMNs are paid 4x blocks in a row") + self.test_hpmmn_payements(window_analysis=256) return - def prepare_hpmn(self, node, idx, alias): - hpmn = HPMN() - hpmn.idx = idx - hpmn.alias = alias - hpmn.p2p_port = p2p_port(hpmn.idx) - - address = node.getnewaddress() - blsKey = node.bls('generate') - hpmn.fundsAddr = address - hpmn.ownerAddr = address - hpmn.operatorAddr = blsKey['public'] - hpmn.votingAddr = address - hpmn.blsMnkey = blsKey['secret'] - - return hpmn - - def register_fund_mn(self, node, mn): - node.sendtoaddress(mn.fundsAddr, 1000.001) - mn.collateral_address = node.getnewaddress() - mn.rewards_address = node.getnewaddress() - - mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) - mn.collateral_txid = mn.protx_hash - mn.collateral_vout = None - - rawtx = node.getrawtransaction(mn.collateral_txid, 1) - for txout in rawtx['vout']: - if txout['value'] == Decimal(1000): - mn.collateral_vout = txout['n'] - break - assert mn.collateral_vout is not None - - self.log.info(">"+str(node.getrawtransaction(mn.protx_hash, 1))) - - def start_mn(self, mn): - self.log.info("len(self.nodes) = " + str(len(self.nodes)) + " mn.idx = " + str(mn.idx)) - if len(self.nodes) <= mn.idx: - self.log.info("len(self.nodes) = " + str(len(self.nodes)) + " mn.idx = " + str(mn.idx)) - self.add_nodes(mn.idx - len(self.nodes) + 1) - # assert len(self.nodes) == mn.idx + 1 - #self.start_node(mn.idx, extra_args = self.extra_args + ['-masternodeblsprivkey=%s' % mn.blsMnkey]) - #force_finish_mnsync(self.nodes[mn.idx]) - #mn.node = self.nodes[mn.idx] - #self.connect_nodes(mn.idx, 0) - #self.sync_all() - def test_hpmmn_payements(self, window_analysis): mn_payees = list() @@ -140,7 +100,7 @@ def test_hpmmn_payements(self, window_analysis): # Skip already checked payee if mn_payees[i].proTxHash == verified_hpmn: continue - # Verify that current HPMN is payed for 4 blocks in a row + # Verify that current HPMN is paid for 4 blocks in a row for j in range(1, 4): # Avoid overflow check if (i + j) < len(mn_payees): @@ -148,7 +108,6 @@ def test_hpmmn_payements(self, window_analysis): verified_hpmn = mn_payees[i].proTxHash def get_mn_payee_for_block(self, block_hash): - mn_payee_info = self.nodes[0].masternode("payments", block_hash)[0] mn_payee_protx = mn_payee_info['masternodes'][0]['proTxHash'] @@ -158,7 +117,7 @@ def get_mn_payee_for_block(self, block_hash): return mn_info return None - def test_quorum_members_are_high_performance(self, llmq_type, quorum_hash): + def test_quorum_members_are_high_performance(self, quorum_hash, llmq_type): quorum_info = self.nodes[0].quorum("info", llmq_type, quorum_hash) quorum_members = extract_quorum_members(quorum_info) mninfos_online = self.mninfo.copy() @@ -171,6 +130,52 @@ def test_quorum_members_are_high_performance(self, llmq_type, quorum_hash): break assert_equal(found, True) + def test_hpmn_protx_are_in_mnlist(self, hpmn_protx_list): + mn_list = self.nodes[0].masternodelist() + for hpmn_protx in hpmn_protx_list: + found = False + for mn in mn_list: + if mn_list.get(mn)['proTxHash'] == hpmn_protx: + found = True + assert_equal(mn_list.get(mn)['type'], "HighPerformance") + assert_equal(found, True) + + def test_hpmn_is_rejected_before_v19(self): + bls = self.nodes[0].bls('generate') + collateral_address = self.nodes[0].getnewaddress() + funds_address = self.nodes[0].getnewaddress() + owner_address = self.nodes[0].getnewaddress() + voting_address = self.nodes[0].getnewaddress() + reward_address = self.nodes[0].getnewaddress() + + collateral_amount = 4000 + collateral_txid = self.nodes[0].sendtoaddress(collateral_address, collateral_amount) + # send to same address to reserve some funds for fees + self.nodes[0].sendtoaddress(funds_address, 1) + collateral_vout = 0 + self.nodes[0].generate(1) + self.sync_all(self.nodes) + + rawtx = self.nodes[0].getrawtransaction(collateral_txid, 1) + for txout in rawtx['vout']: + if txout['value'] == Decimal(collateral_amount): + collateral_vout = txout['n'] + break + assert collateral_vout is not None + + ipAndPort = '127.0.0.1:%d' % p2p_port(len(self.nodes)) + operatorReward = len(self.nodes) + + self.nodes[0].generate(1) + + protx_success = False + try: + self.nodes[0].protx('register', collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True) + protx_success = True + except: + self.log.info("protx_hpmn rejected") + assert_equal(protx_success, False) + if __name__ == '__main__': LLMQHPMNTest().main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 93d7314aeb21..486c469a14a6 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -7,6 +7,7 @@ import configparser import copy +from _decimal import Decimal from enum import Enum import logging import argparse @@ -49,7 +50,7 @@ set_timeout_scale, satoshi_round, wait_until, - get_chain_folder, assert_greater_than_or_equal, + get_chain_folder, rpc_port, ) @@ -469,6 +470,75 @@ def add_nodes(self, num_nodes, extra_args=None, *, rpchost=None, binary=None): use_valgrind=self.options.valgrind, )) + def add_dynamically_node(self, extra_args=None, *, rpchost=None, binary=None): + if self.bind_to_localhost_only: + extra_confs = [["bind=127.0.0.1"]] + else: + extra_confs = [[]] + if extra_args is None: + extra_args = [[]] + if binary is None: + binary = [self.options.bitcoind] + assert_equal(len(extra_confs), 1) + assert_equal(len(binary), 1) + old_num_nodes = len(self.nodes) + + p0 = old_num_nodes + p1 = get_datadir_path(self.options.tmpdir, old_num_nodes) + p2 = self.extra_args_from_options + p3 = self.chain + p4 = rpchost + p5 = self.rpc_timeout + p6 = self.options.timeout_factor + p7 = binary[0] + p8 = self.options.bitcoincli + p9 = self.mocktime + p10 = self.options.coveragedir + p11 = self.options.tmpdir + p12 = extra_confs[0] + p13 = extra_args + p14 = self.options.usecli + p15 = self.options.perf + p16 = self.options.valgrind + + t_node = TestNode(p0, p1, p2, chain=p3, rpchost=p4, timewait=p5, timeout_factor=p6, bitcoind=p7, bitcoin_cli=p8, mocktime=p9, coverage_dir=p10, cwd=p11, extra_conf=p12, extra_args=p13, use_cli=p14, start_perf=p15, use_valgrind=p16) + self.nodes.append(t_node) + return t_node + + def dynamically_initialize_datadir(self, chain, node_p2p_port, node_rpc_port): + data_dir = get_datadir_path(self.options.tmpdir, len(self.nodes)) + if not os.path.isdir(data_dir): + os.makedirs(data_dir) + # Translate chain name to config name + if chain == 'testnet3': + chain_name_conf_arg = 'testnet' + chain_name_conf_section = 'test' + chain_name_conf_arg_value = '1' + elif chain == 'devnet': + chain_name_conf_arg = 'devnet' + chain_name_conf_section = 'devnet' + chain_name_conf_arg_value = 'devnet1' + else: + chain_name_conf_arg = chain + chain_name_conf_section = chain + chain_name_conf_arg_value = '1' + with open(os.path.join(data_dir, "dash.conf"), 'w', encoding='utf8') as f: + f.write("{}={}\n".format(chain_name_conf_arg, chain_name_conf_arg_value)) + f.write("[{}]\n".format(chain_name_conf_section)) + f.write("port=" + str(node_p2p_port) + "\n") + f.write("rpcport=" + str(node_rpc_port) + "\n") + f.write("server=1\n") + f.write("debug=1\n") + f.write("keypool=1\n") + f.write("discover=0\n") + f.write("listenonion=0\n") + f.write("printtoconsole=0\n") + f.write("upnp=0\n") + f.write("natpmp=0\n") + f.write("shrinkdebugfile=0\n") + os.makedirs(os.path.join(data_dir, 'stderr'), exist_ok=True) + os.makedirs(os.path.join(data_dir, 'stdout'), exist_ok=True) + def start_node(self, i, *args, **kwargs): """Start a dashd""" @@ -863,7 +933,6 @@ def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, fas if extra_args is None: extra_args = [[]] * num_nodes assert_equal(len(extra_args), num_nodes) - assert_greater_than_or_equal(masterodes_count, hpmn_count) self.extra_args = [copy.deepcopy(a) for a in extra_args] self.extra_args[0] += ["-sporkkey=cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"] self.fast_dip3_enforcement = fast_dip3_enforcement @@ -956,6 +1025,71 @@ def create_simple_node(self): for i in range(0, idx): self.connect_nodes(i, idx) + def dynamically_add_masternode(self, hpmn=False): + mn_idx = len(self.nodes) + + node_p2p_port = p2p_port(mn_idx) + node_rpc_port = rpc_port(mn_idx) + + created_mn_info = self.dynamically_prepare_masternode(mn_idx, node_p2p_port, hpmn) + + self.dynamically_initialize_datadir(self.nodes[0].chain,node_p2p_port, node_rpc_port) + node_info = self.add_dynamically_node(self.extra_args[1]) + + args = ['-masternodeblsprivkey=%s' % created_mn_info.keyOperator] + node_info.extra_args + self.start_node(mn_idx, args) + + for mn_info in self.mninfo: + if mn_info.proTxHash == created_mn_info.proTxHash: + mn_info.nodeIx = mn_idx + mn_info.node = self.nodes[mn_idx] + + self.connect_nodes(mn_idx, 0) + + self.wait_for_sporks_same() + self.sync_blocks(self.nodes) + force_finish_mnsync(self.nodes[mn_idx]) + + self.log.info("Successfully started and synced proTx:"+str(created_mn_info.proTxHash)) + return created_mn_info.proTxHash + + def dynamically_prepare_masternode(self, idx, node_p2p_port, hpmn=False): + bls = self.nodes[0].bls('generate') + collateral_address = self.nodes[0].getnewaddress() + funds_address = self.nodes[0].getnewaddress() + owner_address = self.nodes[0].getnewaddress() + voting_address = self.nodes[0].getnewaddress() + reward_address = self.nodes[0].getnewaddress() + + collateral_amount = 4000 if hpmn else 1000 + collateral_txid = self.nodes[0].sendtoaddress(collateral_address, collateral_amount) + # send to same address to reserve some funds for fees + self.nodes[0].sendtoaddress(funds_address, 1) + collateral_vout = 0 + self.nodes[0].generate(1) + self.sync_all(self.nodes) + + rawtx = self.nodes[0].getrawtransaction(collateral_txid, 1) + for txout in rawtx['vout']: + if txout['value'] == Decimal(collateral_amount): + collateral_vout = txout['n'] + break + assert collateral_vout is not None + + ipAndPort = '127.0.0.1:%d' % node_p2p_port + operatorReward = idx + + self.nodes[0].generate(1) + protx_result = self.nodes[0].protx('register', collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True) + self.nodes[0].generate(1) + self.sync_all(self.nodes) + mn_info = MasternodeInfo(protx_result, owner_address, voting_address, bls['public'], bls['secret'], collateral_address, collateral_txid, collateral_vout, hpmn) + self.mninfo.append(mn_info) + + mn_type_str = "HPMN" if hpmn else "MN" + self.log.info("Prepared %s %d: collateral_txid=%s, collateral_vout=%d, protxHash=%s" % (mn_type_str, idx, collateral_txid, collateral_vout, protx_result)) + return mn_info + def prepare_masternodes(self): self.log.info("Preparing %d masternodes" % self.mn_count) rewardsAddr = self.nodes[0].getnewaddress() @@ -1001,19 +1135,17 @@ def prepare_masternode(self, idx, rewardsAddr=None, hpmn=False): if register_fund: # self.nodes[0].lockunspent(True, [{'txid': txid, 'vout': collateral_vout}]) - register_fund_rpc = "register_fund_highperf" if hpmn else "register_fund" - protx_result = self.nodes[0].protx(register_fund_rpc, address, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) + requested_fund_amout = 4000 if hpmn else 1000 + protx_result = self.nodes[0].protx('register_fund', address, requested_fund_amout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) else: self.nodes[0].generate(1) - register_rpc = "register_highperf" if hpmn else "register" - protx_result = self.nodes[0].protx(register_rpc, txid, collateral_vout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) + protx_result = self.nodes[0].protx('register', txid, collateral_vout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) if submit: proTxHash = protx_result else: proTxHash = self.nodes[0].sendrawtransaction(protx_result) - if operatorReward > 0: self.nodes[0].generate(1) operatorPayoutAddress = self.nodes[0].getnewaddress() @@ -1022,7 +1154,7 @@ def prepare_masternode(self, idx, rewardsAddr=None, hpmn=False): self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout, hpmn)) # self.sync_all() - mn_type_str = "High performance masternode" if hpmn else "masternode" + mn_type_str = "HPMN" if hpmn else "MN" self.log.info("Prepared %s %d: collateral_txid=%s, collateral_vout=%d, protxHash=%s" % (mn_type_str, idx, txid, collateral_vout, proTxHash)) def remove_masternode(self, idx): @@ -1091,6 +1223,13 @@ def start_masternode(self, mninfo, extra_args=None): mninfo.node = self.nodes[mninfo.nodeIdx] force_finish_mnsync(mninfo.node) + def dynamically_start_masternode(self, mnidx, extra_args=None): + args = [] + if extra_args is not None: + args += extra_args + self.start_node(mnidx, extra_args=args) + force_finish_mnsync(self.nodes[mnidx]) + def setup_network(self): self.log.info("Creating and starting controller node") self.add_nodes(1, extra_args=[self.extra_args[0]]) @@ -1256,6 +1395,7 @@ def wait_for_best_chainlock(self, node, block_hash, timeout=15): def wait_for_sporks_same(self, timeout=30): def check_sporks_same(): + self.bump_mocktime(1) sporks = self.nodes[0].spork('show') return all(node.spork('show') == sporks for node in self.nodes[1:]) wait_until(check_sporks_same, timeout=timeout, sleep=0.5) From 413d43525744157776978cfaaec5d2cea01f7b08 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 26 Jan 2023 00:10:26 +0200 Subject: [PATCH 22/64] Adding of Platform fields --- src/chainparams.cpp | 12 + src/chainparams.h | 4 + src/evo/deterministicmns.cpp | 67 ++- src/evo/deterministicmns.h | 4 +- src/evo/dmnstate.h | 29 +- src/evo/providertx.cpp | 8 +- src/evo/providertx.h | 44 +- src/evo/simplifiedmns.cpp | 13 +- src/evo/simplifiedmns.h | 13 +- src/rpc/evo.cpp | 501 ++++++++++++++++-- src/rpc/masternode.cpp | 5 + .../feature_dip3_deterministicmns.py | 2 +- test/functional/feature_llmq_hpmn.py | 35 +- .../test_framework/test_framework.py | 18 +- 14 files changed, 681 insertions(+), 74 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 2bf5295f4236..4fb54ea35382 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -271,6 +271,9 @@ class CMainParams : public CChainParams { pchMessageStart[2] = 0x6b; pchMessageStart[3] = 0xbd; nDefaultPort = 9999; + //TODO: Determine default values + nDefaultPlatformP2PPort = 22000; + nDefaultPlatformHTTPPort = 22001; nPruneAfterHeight = 100000; m_assumed_blockchain_size = 45; m_assumed_chain_state_size = 1; @@ -502,6 +505,9 @@ class CTestNetParams : public CChainParams { pchMessageStart[2] = 0xca; pchMessageStart[3] = 0xff; nDefaultPort = 19999; + //TODO: Determine default values + nDefaultPlatformP2PPort = 23000; + nDefaultPlatformHTTPPort = 23001; nPruneAfterHeight = 1000; m_assumed_blockchain_size = 4; m_assumed_chain_state_size = 1; @@ -710,6 +716,9 @@ class CDevNetParams : public CChainParams { pchMessageStart[2] = 0xff; pchMessageStart[3] = 0xce; nDefaultPort = 19799; + //TODO: Determine default values + nDefaultPlatformP2PPort = 24000; + nDefaultPlatformHTTPPort = 24001; nPruneAfterHeight = 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; @@ -960,6 +969,9 @@ class CRegTestParams : public CChainParams { pchMessageStart[2] = 0xb7; pchMessageStart[3] = 0xdc; nDefaultPort = 19899; + //TODO: Determine default values + nDefaultPlatformP2PPort = 25000; + nDefaultPlatformHTTPPort = 25001; nPruneAfterHeight = 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; diff --git a/src/chainparams.h b/src/chainparams.h index 18377207f683..a0c8c7275fa3 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -61,6 +61,8 @@ class CChainParams const Consensus::Params& GetConsensus() const { return consensus; } const CMessageHeader::MessageStartChars& MessageStart() const { return pchMessageStart; } uint16_t GetDefaultPort() const { return nDefaultPort; } + uint16_t GetDefaultPlatformP2PPort() const { return nDefaultPlatformP2PPort; } + uint16_t GetDefaultPlatformHTTPPort() const { return nDefaultPlatformHTTPPort; } const CBlock& GenesisBlock() const { return genesis; } const CBlock& DevNetGenesisBlock() const { return devnetGenesis; } @@ -145,6 +147,8 @@ class CChainParams std::vector vSporkAddresses; int nMinSporkKeys; bool fBIP9CheckMasternodesUpgraded; + uint16_t nDefaultPlatformP2PPort; + uint16_t nDefaultPlatformHTTPPort; void AddLLMQ(Consensus::LLMQType llmqType); }; diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 883a1eb9311c..c601bde66650 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -376,10 +376,11 @@ CDeterministicMNListDiff CDeterministicMNList::BuildDiff(const CDeterministicMNL CSimplifiedMNListDiff CDeterministicMNList::BuildSimplifiedDiff(const CDeterministicMNList& to, bool extended) const { + bool v19active = llmq::utils::IsV19Active(::ChainActive().Tip()); CSimplifiedMNListDiff diffRet; diffRet.baseBlockHash = blockHash; diffRet.blockHash = to.blockHash; - diffRet.nVersion = llmq::utils::IsV19Active(::ChainActive().Tip()) ? CSimplifiedMNListDiff::BASIC_BLS_VERSION : CSimplifiedMNListDiff::LEGACY_BLS_VERSION; + diffRet.nVersion = v19active ? CSimplifiedMNListDiff::BASIC_BLS_VERSION : CSimplifiedMNListDiff::LEGACY_BLS_VERSION; to.ForEachMN(false, [&](const auto& toPtr) { auto fromPtr = GetMN(toPtr.proTxHash); @@ -783,6 +784,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } + if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && !llmq::utils::IsV19Active(pindexPrev)) { + return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); + } + if (newList.HasUniqueProperty(proTx.addr) && newList.GetUniquePropertyMN(proTx.addr)->proTxHash != proTx.proTxHash) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_DUPLICATE, "bad-protx-dup-addr"); } @@ -791,10 +796,21 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C if (!dmn) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash"); } + if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->nType != CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type"); + } + if (proTx.nType == CProUpServTx::TYPE_REGULAR_MASTERNODE && dmn->nType != CDeterministicMN::TYPE_REGULAR_MASTERNODE) { + return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type"); + } + auto newState = std::make_shared(*dmn->pdmnState); newState->addr = proTx.addr; newState->scriptOperatorPayout = proTx.scriptOperatorPayout; - + if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + newState->platformNodeID = proTx.platformNodeID; + newState->platformP2PPort = proTx.platformP2PPort; + newState->platformHTTPPort = proTx.platformHTTPPort; + } if (newState->IsBanned()) { // only revive when all keys are set if (newState->pubKeyOperator.Get().IsValid() && !newState->keyIDVoting.IsNull() && !newState->keyIDOwner.IsNull()) { @@ -1248,6 +1264,41 @@ static bool CheckService(const ProTx& proTx, CValidationState& state) return true; } +template +static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state, bool allowNullValues) +{ + // platformNodeID can be null since it can not be known at this time (v19 HPMNs registration) + if (!allowNullValues && proTx.platformNodeID.IsNull()) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-nodeid"); + } + + static int mainnetPlatformP2PPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPlatformP2PPort(); + if (Params().NetworkIDString() == CBaseChainParams::MAIN) { + if (proTx.platformP2PPort != mainnetPlatformP2PPort) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-p2p-port"); + } + } else if (proTx.platformP2PPort == mainnetPlatformP2PPort || proTx.platformP2PPort < 1 || proTx.platformP2PPort > std::numeric_limits::max()) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-p2p-port"); + } + + static int mainnetPlatformHTTPPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPlatformHTTPPort(); + if (Params().NetworkIDString() == CBaseChainParams::MAIN) { + if (proTx.platformHTTPPort != mainnetPlatformHTTPPort) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-http-port"); + } + } else if (proTx.platformHTTPPort == mainnetPlatformHTTPPort || proTx.platformHTTPPort < 1 || proTx.platformHTTPPort > std::numeric_limits::max()) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-http-port"); + } + + if (proTx.platformP2PPort == proTx.platformHTTPPort || + proTx.platformP2PPort == proTx.addr.GetPort() || + proTx.platformHTTPPort == proTx.addr.GetPort()) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-dup-ports"); + } + + return true; +} + template static bool CheckHashSig(const ProTx& proTx, const PKHash& pkhash, CValidationState& state) { @@ -1299,6 +1350,12 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid return false; } + if (ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (!CheckPlatformFields(ptx, state, true)) { + return false; + } + } + CTxDestination collateralTxDest; const PKHash *keyForPayloadSig = nullptr; COutPoint collateralOutpoint; @@ -1404,6 +1461,12 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa return false; } + if (ptx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (!CheckPlatformFields(ptx, state, true)) { + return false; + } + } + if (pindexPrev) { auto mnList = deterministicMNManager->GetListForBlock(pindexPrev); auto mn = mnList.GetMN(ptx.proTxHash); diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index b7ee896d2d17..2663fbd05f70 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -484,9 +484,9 @@ class CDeterministicMNListDiff tmp = ReadCompactSize(s); for (size_t i = 0; i < tmp; i++) { CDeterministicMNStateDiff diff; - // CDeterministicMNState holds a new field (nConsecutivePayments) but no migration is needed here since: + // CDeterministicMNState hold new fields {nConsecutivePayments, platformNodeID, platformP2PPort, platformHTTPPort} but no migration is needed here since: // CDeterministicMNStateDiff is always serialised using a bitmask. - // Because the new field (nConsecutivePayments) has a new bit guide value then we are good to continue + // Because the new field have a new bit guide value then we are good to continue tmp2 = ReadVarInt(s); s >> diff; updatedMNs.emplace(tmp2, std::move(diff)); diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index adaa4d397a1a..c31d3e83bb39 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -53,13 +53,20 @@ class CDeterministicMNState CScript scriptPayout; CScript scriptOperatorPayout; + uint160 platformNodeID{}; + uint16_t platformP2PPort{0}; + uint16_t platformHTTPPort{0}; + public: CDeterministicMNState() = default; explicit CDeterministicMNState(const CProRegTx& proTx) : keyIDOwner(proTx.keyIDOwner), keyIDVoting(proTx.keyIDVoting), addr(proTx.addr), - scriptPayout(proTx.scriptPayout) + scriptPayout(proTx.scriptPayout), + platformNodeID(proTx.platformNodeID), + platformP2PPort(proTx.platformP2PPort), + platformHTTPPort(proTx.platformHTTPPort) { pubKeyOperator.Set(proTx.pubKeyOperator); } @@ -86,7 +93,10 @@ class CDeterministicMNState obj.keyIDVoting, obj.addr, obj.scriptPayout, - obj.scriptOperatorPayout + obj.scriptOperatorPayout, + obj.platformNodeID, + obj.platformP2PPort, + obj.platformHTTPPort ); } @@ -96,6 +106,7 @@ class CDeterministicMNState addr = CService(); scriptOperatorPayout = CScript(); nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED; + platformNodeID = uint160(); } void BanIfNotBanned(int height) { @@ -150,9 +161,12 @@ class CDeterministicMNStateDiff Field_scriptPayout = 0x1000, Field_scriptOperatorPayout = 0x2000, Field_nConsecutivePayments = 0x4000, + Field_platformNodeID = 0x8000, + Field_platformP2PPort = 0x10000, + Field_platformHTTPPort = 0x20000, }; -#define DMN_STATE_DIFF_ALL_FIELDS_BUT_CONSECUTIVE_PAYMENTS \ +#define DMN_STATE_DIFF_ALL_FIELDS \ DMN_STATE_DIFF_LINE(nRegisteredHeight) \ DMN_STATE_DIFF_LINE(nLastPaidHeight) \ DMN_STATE_DIFF_LINE(nPoSePenalty) \ @@ -166,11 +180,10 @@ class CDeterministicMNStateDiff DMN_STATE_DIFF_LINE(keyIDVoting) \ DMN_STATE_DIFF_LINE(addr) \ DMN_STATE_DIFF_LINE(scriptPayout) \ - DMN_STATE_DIFF_LINE(scriptOperatorPayout) - -#define DMN_STATE_DIFF_ALL_FIELDS \ - DMN_STATE_DIFF_ALL_FIELDS_BUT_CONSECUTIVE_PAYMENTS \ - DMN_STATE_DIFF_LINE(nConsecutivePayments) \ + DMN_STATE_DIFF_LINE(scriptOperatorPayout) \ + DMN_STATE_DIFF_LINE(platformNodeID) \ + DMN_STATE_DIFF_LINE(platformP2PPort) \ + DMN_STATE_DIFF_LINE(platformHTTPPort) public: uint32_t fields{0}; diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 4439e673a8f4..4ece3113aa70 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -78,8 +78,8 @@ std::string CProRegTx::ToString() const payee = EncodeDestination(dest); } - return strprintf("CProRegTx(nVersion=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s)", - nVersion, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee); + return strprintf("CProRegTx(nVersion=%d, nType=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s, platformNodeID=%s, platformP2PPort=%d, platformHTTPPort=%d)", + nVersion, nType, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); } maybe_error CProUpServTx::IsTriviallyValid(bool is_bls_legacy_scheme) const @@ -99,8 +99,8 @@ std::string CProUpServTx::ToString() const payee = EncodeDestination(dest); } - return strprintf("CProUpServTx(nVersion=%d, proTxHash=%s, addr=%s, operatorPayoutAddress=%s)", - nVersion, proTxHash.ToString(), addr.ToString(), payee); + return strprintf("CProUpServTx(nVersion=%d, nType=%d, proTxHash=%s, addr=%s, operatorPayoutAddress=%s, platformNodeID=%s, platformP2PPort=%d, platformHTTPPort=%d)", + nVersion, nType, proTxHash.ToString(), addr.ToString(), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); } maybe_error CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 7c2e3933c8d9..3c364d956319 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -39,6 +39,9 @@ class CProRegTx uint16_t nMode{0}; // only 0 supported for now COutPoint collateralOutpoint{uint256(), (uint32_t)-1}; // if hash is null, we refer to a ProRegTx output CService addr; + uint160 platformNodeID{}; + uint16_t platformP2PPort{0}; + uint16_t platformHTTPPort{0}; CKeyID keyIDOwner; CBLSPublicKey pubKeyOperator; CKeyID keyIDVoting; @@ -50,14 +53,14 @@ class CProRegTx SERIALIZE_METHODS(CProRegTx, obj) { READWRITE( - obj.nVersion + obj.nVersion, + obj.nType ); if (obj.nVersion == 0 || obj.nVersion > BASIC_BLS_VERSION) { // unknown version, bail out early return; } READWRITE( - obj.nType, obj.nMode, obj.collateralOutpoint, obj.addr, @@ -68,6 +71,13 @@ class CProRegTx obj.scriptPayout, obj.inputsHash ); + if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + READWRITE( + obj.platformNodeID, + obj.platformP2PPort, + obj.platformHTTPPort + ); + } if (!(s.GetType() & SER_GETHASH)) { READWRITE(obj.vchSig); } @@ -97,7 +107,11 @@ class CProRegTx } obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION)); obj.pushKV("operatorReward", (double)nOperatorReward / 100); - + if (nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + obj.pushKV("platformNodeID", platformNodeID.ToString()); + obj.pushKV("platformP2PPort", platformP2PPort); + obj.pushKV("platformHTTPPort", platformHTTPPort); + } obj.pushKV("inputsHash", inputsHash.ToString()); } @@ -110,6 +124,8 @@ class CProUpServTx static constexpr auto SPECIALTX_TYPE = TRANSACTION_PROVIDER_UPDATE_SERVICE; static constexpr uint16_t LEGACY_BLS_VERSION = 1; static constexpr uint16_t BASIC_BLS_VERSION = 2; + static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; + static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; [[nodiscard]] static constexpr auto GetVersion(const bool is_basic_scheme_active) -> uint16_t { @@ -117,8 +133,12 @@ class CProUpServTx } uint16_t nVersion{LEGACY_BLS_VERSION}; // message version + uint16_t nType{TYPE_REGULAR_MASTERNODE}; uint256 proTxHash; CService addr; + uint160 platformNodeID{}; + uint16_t platformP2PPort{0}; + uint16_t platformHTTPPort{0}; CScript scriptOperatorPayout; uint256 inputsHash; // replay protection CBLSSignature sig; @@ -132,12 +152,24 @@ class CProUpServTx // unknown version, bail out early return; } + if (obj.nVersion == BASIC_BLS_VERSION) { + READWRITE( + obj.nType + ); + } READWRITE( obj.proTxHash, obj.addr, obj.scriptOperatorPayout, obj.inputsHash ); + if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + READWRITE( + obj.platformNodeID, + obj.platformP2PPort, + obj.platformHTTPPort + ); + } if (!(s.GetType() & SER_GETHASH)) { READWRITE( CBLSSignatureVersionWrapper(const_cast(obj.sig), (obj.nVersion == LEGACY_BLS_VERSION), true) @@ -152,12 +184,18 @@ class CProUpServTx obj.clear(); obj.setObject(); obj.pushKV("version", nVersion); + obj.pushKV("type", nType); obj.pushKV("proTxHash", proTxHash.ToString()); obj.pushKV("service", addr.ToString(false)); CTxDestination dest; if (ExtractDestination(scriptOperatorPayout, dest)) { obj.pushKV("operatorPayoutAddress", EncodeDestination(dest)); } + if (nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + obj.pushKV("platformNodeID", platformNodeID.ToString()); + obj.pushKV("platformP2PPort", platformP2PPort); + obj.pushKV("platformHTTPPort", platformHTTPPort); + } obj.pushKV("inputsHash", inputsHash.ToString()); } diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 926a75eddd73..3fd80e1fab10 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -30,7 +30,9 @@ CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) : keyIDVoting(dmn.pdmnState->keyIDVoting), isValid(!dmn.pdmnState->IsBanned()), scriptPayout(dmn.pdmnState->scriptPayout), - scriptOperatorPayout(dmn.pdmnState->scriptOperatorPayout) + scriptOperatorPayout(dmn.pdmnState->scriptOperatorPayout), + nType(dmn.nType), + platformHTTPPort(dmn.pdmnState->platformHTTPPort) { } @@ -53,8 +55,8 @@ std::string CSimplifiedMNListEntry::ToString() const operatorPayoutAddress = EncodeDestination(dest); } - return strprintf("CSimplifiedMNListEntry(proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s)", - proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress); + return strprintf("CSimplifiedMNListEntry(nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d)", + nType, proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort); } void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const @@ -68,6 +70,10 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const obj.pushKV("votingAddress", EncodeDestination(PKHash(keyIDVoting))); obj.pushKV("isValid", isValid); obj.pushKV("nVersion", nVersion); + obj.pushKV("nType", nType); + if (nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + obj.pushKV("platformHTTPPort", platformHTTPPort); + } if (!extended) return; @@ -80,6 +86,7 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const } } +//TODO: Invistigate if we can delete this constructor CSimplifiedMNList::CSimplifiedMNList(const std::vector& smlEntries) { mnList.resize(smlEntries.size()); diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 95cb4aa3df8d..2f67cc96e404 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -6,6 +6,7 @@ #define BITCOIN_EVO_SIMPLIFIEDMNS_H #include +#include #include #include #include @@ -32,6 +33,8 @@ class CSimplifiedMNListEntry CBLSLazyPublicKey pubKeyOperator; CKeyID keyIDVoting; bool isValid{false}; + uint16_t nType{CDeterministicMN::TYPE_REGULAR_MASTERNODE}; + uint16_t platformHTTPPort{0}; CScript scriptPayout; // mem-only CScript scriptOperatorPayout; // mem-only uint16_t nVersion{LEGACY_BLS_VERSION}; // mem-only @@ -47,7 +50,9 @@ class CSimplifiedMNListEntry pubKeyOperator == rhs.pubKeyOperator && keyIDVoting == rhs.keyIDVoting && isValid == rhs.isValid && - nVersion == rhs.nVersion; + nVersion == rhs.nVersion && + nType == rhs.nType && + platformHTTPPort == rhs.platformHTTPPort; } bool operator!=(const CSimplifiedMNListEntry& rhs) const @@ -65,6 +70,12 @@ class CSimplifiedMNListEntry obj.keyIDVoting, obj.isValid ); + if (obj.nVersion == BASIC_BLS_VERSION) { + READWRITE(obj.nType); + if (obj.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + READWRITE(obj.platformHTTPPort); + } + } } uint256 CalcHash() const; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 48398f9b4f30..246f94199b07 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -47,10 +47,6 @@ static RPCArg GetRpcArg(const std::string& strParamName) {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, "The dash address to send the collateral to."} }, - {"collateralAmount", - {"collateralAmount", RPCArg::Type::NUM, RPCArg::Optional::NO, - "The collateral amount. Possible values 1000 (MN) or 4000 (HPMN)."} - }, {"collateralHash", {"collateralHash", RPCArg::Type::STR, RPCArg::Optional::NO, "The collateral transaction hash."} @@ -142,6 +138,18 @@ static RPCArg GetRpcArg(const std::string& strParamName) "It has to match the private key which is later used when voting on proposals.\n" "If set to an empty string, the currently active voting key address is reused."} }, + {"platformNodeID", + {"platformNodeID", RPCArg::Type::STR, RPCArg::Optional::NO, + "Platform P2P node ID, derived from P2P public key."} + }, + {"platformP2PPort", + {"platformP2PPort", RPCArg::Type::NUM, RPCArg::Optional::NO, + "TCP port of Dash Platform peer-to-peer communication between nodes (network byte order)."} + }, + {"platformHTTPPort", + {"platformHTTPPort", RPCArg::Type::NUM, RPCArg::Optional::NO, + "TCP port of Platform HTTP/API interface (network byte order). "} + }, }; auto it = mapParamHelp.find(strParamName); @@ -181,6 +189,13 @@ static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey, const std::str return secKey; } +static bool ValidatePort(const int32_t port) +{ + if (port < 1 || port > std::numeric_limits::max()) + return false; + return true; +} + #ifdef ENABLE_WALLET template @@ -328,16 +343,14 @@ static std::string SignAndSendSpecialTx(const JSONRPCRequest& request, const CMu static void protx_register_fund_help(const JSONRPCRequest& request) { RPCHelpMan{"protx register_fund", - "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move requested collateralAmount Dash\n" + "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 1000 Dash\n" "to the address specified by collateralAddress and will then function as the collateral of your\n" "masternode.\n" - "If the collaretalAmount is set to 4000 it will lead to a HPMN fund registration. Otherwise, 1000 will be required for a regular MN." "A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n" "is fully deployed.\n" + HELP_REQUIRING_PASSPHRASE, { GetRpcArg("collateralAddress"), - GetRpcArg("collateralAmount"), GetRpcArg("ipAndPort"), GetRpcArg("ownerAddress"), GetRpcArg("operatorPubKey_register"), @@ -354,7 +367,7 @@ static void protx_register_fund_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" 1000 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } @@ -364,7 +377,6 @@ static void protx_register_help(const JSONRPCRequest& request) RPCHelpMan{"protx register", "\nSame as \"protx register_fund\", but with an externally referenced collateral.\n" "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" - "If the unspent collateral are 4000 DASH this will lead to a HPMN registration. Otherwise, 1000 for a usual masternode registration" "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" + HELP_REQUIRING_PASSPHRASE, { @@ -396,7 +408,6 @@ static void protx_register_prepare_help(const JSONRPCRequest& request) RPCHelpMan{"protx register_prepare", "\nCreates an unsigned ProTx and a message that must be signed externally\n" "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" - "If the unspent collateral are 4000 DASH this will lead to a HPMN registration. Otherwise, 1000 for a usual masternode registration" "The prepared transaction will also contain inputs and outputs to cover fees.\n", { GetRpcArg("collateralHash"), @@ -476,28 +487,16 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; else ptx.nVersion = CProRegTx::GetVersion(isV19active); - ptx.nType = CProRegTx::TYPE_REGULAR_MASTERNODE; //Will be eventually updated later + ptx.nType = CProRegTx::TYPE_REGULAR_MASTERNODE; - CAmount fundRequestedAmount = 0; //Will be updated later + CAmount collateralAmount = 1000 * COIN; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); if (!IsValidDestination(collateralDest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); } - paramIdx++; - int32_t fundRequestedCollateralAmount = ParseInt32V(request.params[paramIdx], "collateralAmount"); - if (fundRequestedCollateralAmount != 1000 && fundRequestedCollateralAmount != 4000) { - throw JSONRPCError(RPC_INVALID_REQUEST, "invalid collateralAmount: must be either 1000 or 4000."); - } - if (fundRequestedCollateralAmount == 4000) { - if (!isV19active) { - throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); - } - ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; - } CScript collateralScript = GetScriptForDestination(collateralDest); - fundRequestedAmount = fundRequestedCollateralAmount * COIN; - CTxOut collateralTxOut(fundRequestedAmount, collateralScript); + CTxOut collateralTxOut(collateralAmount, collateralScript); tx.vout.emplace_back(collateralTxOut); paramIdx++; @@ -571,7 +570,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, if (isFundRegister) { uint32_t collateralIndex = (uint32_t) -1; for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == fundRequestedAmount) { + if (tx.vout[i].nValue == collateralAmount) { collateralIndex = i; break; } @@ -579,8 +578,6 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, CHECK_NONFATAL(collateralIndex != (uint32_t) -1); ptx.collateralOutpoint.n = collateralIndex; - LogPrintf("takis fund ptx[%d] fundRequestedAmount[%d]\n", ptx.nType, fundRequestedAmount); - SetTxPayload(tx, ptx); return SignAndSendSpecialTx(request, tx, fSubmit); } else { @@ -590,13 +587,6 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort())); } - CAmount HPMNCollateralAmount = 4000 * COIN; - if (coin.out.nValue == HPMNCollateralAmount) { - if (!isV19active) { - throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); - } - ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; - } CTxDestination txDest; ExtractDestination(coin.out.scriptPubKey, txDest); const PKHash *pkhash = std::get_if(&txDest); @@ -604,8 +594,6 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); } - LogPrintf("takis non-fund ptx[%d] coint_value[%d]\n", ptx.nType, coin.out.nValue); - if (isPrepareRegister) { // external signing with collateral key ptx.vchSig.clear(); @@ -634,6 +622,306 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, } } +static void protx_register_fund_hpmn_help(const JSONRPCRequest& request) +{ + RPCHelpMan{"protx register_fund_hpmn", + "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 4000 Dash\n" + "to the address specified by collateralAddress and will then function as the collateral of your\n" + "HPMN.\n" + "A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n" + "is fully deployed.\n" + + HELP_REQUIRING_PASSPHRASE, + { + GetRpcArg("collateralAddress"), + GetRpcArg("ipAndPort"), + GetRpcArg("ownerAddress"), + GetRpcArg("operatorPubKey_register"), + GetRpcArg("votingAddress_register"), + GetRpcArg("operatorReward"), + GetRpcArg("payoutAddress_register"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("fundAddress"), + GetRpcArg("submit"), + }, + { + RPCResult{"if \"submit\" is not set or set to true", + RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + RPCResult{"if \"submit\" is set to false", + RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, + }, + RPCExamples{ + HelpExampleCli("protx", "register_fund_hpmn \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" 1000 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") + }, + }.Check(request); +} + +static void protx_register_hpmn_help(const JSONRPCRequest& request) +{ + RPCHelpMan{"protx register_hpmn", + "\nSame as \"protx register_fund_hpmn\", but with an externally referenced collateral.\n" + "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" + "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" + + HELP_REQUIRING_PASSPHRASE, + { + GetRpcArg("collateralHash"), + GetRpcArg("collateralIndex"), + GetRpcArg("ipAndPort"), + GetRpcArg("ownerAddress"), + GetRpcArg("operatorPubKey_register"), + GetRpcArg("votingAddress_register"), + GetRpcArg("operatorReward"), + GetRpcArg("payoutAddress_register"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("feeSourceAddress"), + GetRpcArg("submit"), + }, + { + RPCResult{"if \"submit\" is not set or set to true", + RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + RPCResult{"if \"submit\" is set to false", + RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, + }, + RPCExamples{ + HelpExampleCli("protx", "register_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") + }, + }.Check(request); +} + +static void protx_register_prepare_hpmn_help(const JSONRPCRequest& request) +{ + RPCHelpMan{"protx register_prepare_hpmn", + "\nCreates an unsigned ProTx and a message that must be signed externally\n" + "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" + "The prepared transaction will also contain inputs and outputs to cover fees.\n", + { + GetRpcArg("collateralHash"), + GetRpcArg("collateralIndex"), + GetRpcArg("ipAndPort"), + GetRpcArg("ownerAddress"), + GetRpcArg("operatorPubKey_register"), + GetRpcArg("votingAddress_register"), + GetRpcArg("operatorReward"), + GetRpcArg("payoutAddress_register"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("feeSourceAddress"), + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", + { + {RPCResult::Type::STR_HEX, "tx", "The serialized unsigned ProTx in hex format"}, + {RPCResult::Type::STR_HEX, "collateralAddress", "The collateral address"}, + {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, + }}, + RPCExamples{ + HelpExampleCli("protx", "register_prepare_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") + }, + }.Check(request); +} + +static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, + const bool isExternalRegister, + const bool isFundRegister, + const bool isPrepareRegister) +{ + if (isFundRegister && (request.fHelp || (request.params.size() < 10 || request.params.size() > 12))) { + protx_register_fund_hpmn_help(request); + } else if (isExternalRegister && (request.fHelp || (request.params.size() < 11 || request.params.size() > 13))) { + protx_register_hpmn_help(request); + } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 11 && request.params.size() != 12))) { + protx_register_prepare_hpmn_help(request); + } + + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); + if (!wallet) return NullUniValue; + + if (isExternalRegister || isFundRegister) { + EnsureWalletIsUnlocked(wallet.get()); + } + + bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); + if (!isV19active) { + throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); + } + + size_t paramIdx = 0; + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_REGISTER; + + CProRegTx ptx; + ptx.nVersion = CProRegTx::GetVersion(isV19active); + ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + + CAmount requestedCollateralAmount = 4000 * COIN; + if (isFundRegister) { + CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); + if (!IsValidDestination(collateralDest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); + } + CScript collateralScript = GetScriptForDestination(collateralDest); + CTxOut collateralTxOut(requestedCollateralAmount, collateralScript); + tx.vout.emplace_back(collateralTxOut); + + paramIdx++; + } else { + uint256 collateralHash = ParseHashV(request.params[paramIdx], "collateralHash"); + int32_t collateralIndex = ParseInt32V(request.params[paramIdx + 1], "collateralIndex"); + if (collateralHash.IsNull() || collateralIndex < 0) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex)); + } + + ptx.collateralOutpoint = COutPoint(collateralHash, (uint32_t)collateralIndex); + paramIdx += 2; + + // TODO unlock on failure + LOCK(wallet->cs_wallet); + wallet->LockCoin(ptx.collateralOutpoint); + } + + if (request.params[paramIdx].get_str() != "") { + if (!Lookup(request.params[paramIdx].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { + throw std::runtime_error(strprintf("invalid network address %s", request.params[paramIdx].get_str())); + } + } + + ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address"); + CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address"); + CKeyID keyIDVoting = ptx.keyIDOwner; + + if (request.params[paramIdx + 3].get_str() != "") { + keyIDVoting = ParsePubKeyIDFromAddress(request.params[paramIdx + 3].get_str(), "voting address"); + } + + int64_t operatorReward; + if (!ParseFixedPoint(request.params[paramIdx + 4].getValStr(), 2, &operatorReward)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be a number"); + } + if (operatorReward < 0 || operatorReward > 10000) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be between 0.00 and 100.00"); + } + ptx.nOperatorReward = operatorReward; + + CTxDestination payoutDest = DecodeDestination(request.params[paramIdx + 5].get_str()); + if (!IsValidDestination(payoutDest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); + } + + // platformNodeID can be null in ProRegTx since it can not be known at this time (v19 HPMNs registration) + if (request.params[paramIdx + 6].get_str().empty()) { + ptx.platformNodeID.SetNull(); + } + else { + if (!IsHex(request.params[paramIdx + 6].get_str())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); + ptx.platformNodeID.SetHex(request.params[paramIdx + 6].get_str()); + } + + int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 7].get_str(), "platformP2PPort"); + if (!ValidatePort(requestedPlatformP2PPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); + } + ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); + + int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[paramIdx + 8].get_str(), "platformHTTPPort"); + if (!ValidatePort(requestedPlatformHTTPPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); + } + ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); + + ptx.pubKeyOperator = pubKeyOperator; + ptx.keyIDVoting = keyIDVoting; + ptx.scriptPayout = GetScriptForDestination(payoutDest); + + if (!isFundRegister) { + // make sure fee calculation works + ptx.vchSig.resize(65); + } + + CTxDestination fundDest = payoutDest; + if (!request.params[paramIdx + 9].isNull()) { + fundDest = DecodeDestination(request.params[paramIdx + 9].get_str()); + if (!IsValidDestination(fundDest)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 9].get_str()); + } + + FundSpecialTx(wallet.get(), tx, ptx, fundDest); + UpdateSpecialTxInputsHash(tx, ptx); + + bool fSubmit{true}; + if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 10].isNull()) { + fSubmit = ParseBoolV(request.params[paramIdx + 10], "submit"); + } + + if (isFundRegister) { + uint32_t collateralIndex = (uint32_t) -1; + for (uint32_t i = 0; i < tx.vout.size(); i++) { + if (tx.vout[i].nValue == requestedCollateralAmount) { + collateralIndex = i; + break; + } + } + CHECK_NONFATAL(collateralIndex != (uint32_t) -1); + ptx.collateralOutpoint.n = collateralIndex; + + SetTxPayload(tx, ptx); + return SignAndSendSpecialTx(request, tx, fSubmit); + } else { + // referencing external collateral + + Coin coin; + if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort())); + } + CTxDestination txDest; + ExtractDestination(coin.out.scriptPubKey, txDest); + const CKeyID *keyID = std::get_if(&txDest); + if (!keyID) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); + } + + if (isPrepareRegister) { + // external signing with collateral key + ptx.vchSig.clear(); + SetTxPayload(tx, ptx); + + UniValue ret(UniValue::VOBJ); + ret.pushKV("tx", EncodeHexTx(CTransaction(tx))); + ret.pushKV("collateralAddress", EncodeDestination(txDest)); + ret.pushKV("signMessage", ptx.MakeSignString()); + return ret; + } else { + // lets prove we own the collateral + LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan(); + if (!spk_man) { + throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command"); + } + + CKey key; + if (!spk_man->GetKey(*keyID, key)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest))); + } + SignSpecialTxPayloadByString(tx, ptx, key); + SetTxPayload(tx, ptx); + return SignAndSendSpecialTx(request, tx, fSubmit); + } + } +} + +static UniValue protx_register_hpmn(const JSONRPCRequest& request) +{ + bool isExternalRegister = request.strMethod == "protxregister_hpmn"; + bool isFundRegister = request.strMethod == "protxregister_fund_hpmn"; + bool isPrepareRegister = request.strMethod == "protxregister_prepare_hpmn"; + return protx_register_hpmn_wrapper(request, isExternalRegister, isFundRegister, isPrepareRegister); +} + static UniValue protx_register(const JSONRPCRequest& request) { bool isExternalRegister = request.strMethod == "protxregister"; @@ -726,6 +1014,9 @@ static UniValue protx_update_service(const JSONRPCRequest& request) if (!dmn) { throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); } + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + throw std::runtime_error(strprintf("masternode with proTxHash %s is a HPMN", ptx.proTxHash.ToString())); + } if (keyOperator.GetPublicKey() != dmn->pdmnState->pubKeyOperator.Get()) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("the operator key does not belong to the registered public key")); @@ -775,6 +1066,134 @@ static UniValue protx_update_service(const JSONRPCRequest& request) return SignAndSendSpecialTx(request, tx); } +static void protx_update_service_hpmn_help(const JSONRPCRequest& request) +{ + RPCHelpMan{"protx update_service_hpmn", + "\nCreates and sends a ProUpServTx to the network. This will update the IP address and the Platform fields\n" + "of a HPMN.\n" + "If this is done for a HPMN that got PoSe-banned, the ProUpServTx will also revive this HPMN.\n" + + HELP_REQUIRING_PASSPHRASE, + { + GetRpcArg("proTxHash"), + GetRpcArg("ipAndPort"), + GetRpcArg("operatorKey"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("operatorPayoutAddress"), + GetRpcArg("feeSourceAddress"), + }, + RPCResult{ + RPCResult::Type::STR_HEX, "txid", "The transaction id" + }, + RPCExamples{ + HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") + }, + }.Check(request); +} +static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) +{ + protx_update_service_hpmn_help(request); + + std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); + if (!wallet) return NullUniValue; + + EnsureWalletIsUnlocked(wallet.get()); + + bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); + if (!isV19active) { + throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); + } + + CProUpServTx ptx; + ptx.nVersion = CProUpServTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); + ptx.nType = CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + ptx.proTxHash = ParseHashV(request.params[0], "proTxHash"); + + if (!Lookup(request.params[1].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { + throw std::runtime_error(strprintf("invalid network address %s", request.params[1].get_str())); + } + + CBLSSecretKey keyOperator = ParseBLSSecretKey(request.params[2].get_str(), "operatorKey"); + + // platformNodeID can be null since it can not be known at this time (v19 HPMNs registration) + if (request.params[3].get_str().empty()) { + ptx.platformNodeID.SetNull(); + } + else { + if (!IsHex(request.params[3].get_str())) + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); + ptx.platformNodeID.SetHex(request.params[3].get_str()); + } + + int32_t requestedPlatformP2PPort = ParseInt32V(request.params[4].get_str(), "platformP2PPort"); + if (!ValidatePort(requestedPlatformP2PPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); + } + ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); + + int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[5].get_str(), "platformHTTPPort"); + if (!ValidatePort(requestedPlatformHTTPPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); + } + ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); + + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash); + if (!dmn) { + throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); + } + if (dmn->nType != CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + throw std::runtime_error(strprintf("masternode with proTxHash %s is not a HPMN", ptx.proTxHash.ToString())); + } + + if (keyOperator.GetPublicKey() != dmn->pdmnState->pubKeyOperator.Get()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("the operator key does not belong to the registered public key")); + } + + CMutableTransaction tx; + tx.nVersion = 3; + tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; + + // param operatorPayoutAddress + if (!request.params[6].isNull()) { + if (request.params[6].get_str().empty()) { + ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; + } else { + CTxDestination payoutDest = DecodeDestination(request.params[6].get_str()); + if (!IsValidDestination(payoutDest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[6].get_str())); + } + ptx.scriptOperatorPayout = GetScriptForDestination(payoutDest); + } + } else { + ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; + } + + CTxDestination feeSource; + + // param feeSourceAddress + if (!request.params[7].isNull()) { + feeSource = DecodeDestination(request.params[7].get_str()); + if (!IsValidDestination(feeSource)) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[7].get_str()); + } else { + if (ptx.scriptOperatorPayout != CScript()) { + // use operator reward address as default source for fees + ExtractDestination(ptx.scriptOperatorPayout, feeSource); + } else { + // use payout address as default source for fees + ExtractDestination(dmn->pdmnState->scriptPayout, feeSource); + } + } + + FundSpecialTx(wallet.get(), tx, ptx, feeSource); + + SignSpecialTxPayloadByHash(tx, ptx, keyOperator); + SetTxPayload(tx, ptx); + + return SignAndSendSpecialTx(request, tx); +} + static void protx_update_registrar_help(const JSONRPCRequest& request) { RPCHelpMan{"protx update_registrar", @@ -1268,6 +1687,9 @@ static UniValue protx_diff(const JSONRPCRequest& request) " register - Create and send ProTx to network\n" " register_fund - Fund, create and send ProTx to network\n" " register_prepare - Create an unsigned ProTx\n" + " register_hpmn - Create and send ProTx to network for a HPMN\n" + " register_fund_hpmn - Fund, create and send ProTx to network for a HPMN\n" + " register_prepare_hpmn - Create an unsigned ProTx for a HPMN\n" " register_legacy - Create a ProTx by parsing BLS using the legacy scheme and send it to network\n" " register_fund_legacy - Fund and create a ProTx by parsing BLS using the legacy scheme, then send it to network\n" " register_prepare_legacy - Create an unsigned ProTx by parsing BLS using the legacy scheme\n" @@ -1277,6 +1699,7 @@ static UniValue protx_diff(const JSONRPCRequest& request) " info - Return information about a ProTx\n" #ifdef ENABLE_WALLET " update_service - Create and send ProUpServTx to network\n" + " update_service_hpmn - Create and send ProUpServTx to network for a HPMN\n" " update_registrar - Create and send ProUpRegTx to network\n" " revoke - Create and send ProUpRevTx to network\n" #endif @@ -1297,12 +1720,16 @@ static UniValue protx(const JSONRPCRequest& request) #ifdef ENABLE_WALLET if (command == "protxregister" || command == "protxregister_fund" || command == "protxregister_prepare") { return protx_register(new_request); + } else if (command == "protxregister_hpmn" || command == "protxregister_fund_hpmn" || command == "protxregister_prepare_hpmn") { + return protx_register_hpmn(new_request); } else if (command == "protxregister_legacy" || command == "protxregister_fund_legacy" || command == "protxregister_prepare_legacy") { return protx_register_legacy(new_request); } else if (command == "protxregister_submit") { return protx_register_submit(new_request); } else if (command == "protxupdate_service") { return protx_update_service(new_request); + } else if (command == "protxupdate_service_hpmn") { + return protx_update_service_hpmn(new_request); } else if (command == "protxupdate_registrar") { return protx_update_registrar(new_request); } else if (command == "protxupdate_registrar_legacy") { diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index a3213a9cbaaa..7703ab3abf4f 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -667,6 +667,11 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("payee", payeeStr); objMN.pushKV("status", dmnToStatus(dmn)); objMN.pushKV("type", dmn.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); + if (dmn.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + objMN.pushKV("platformNodeID", dmn.pdmnState->platformNodeID.ToString()); + objMN.pushKV("platformP2PPort", dmn.pdmnState->platformP2PPort); + objMN.pushKV("platformHTTPPort", dmn.pdmnState->platformHTTPPort); + } objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty); objMN.pushKV("nConsecutivePayments", dmn.pdmnState->nConsecutivePayments); objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn)); diff --git a/test/functional/feature_dip3_deterministicmns.py b/test/functional/feature_dip3_deterministicmns.py index da7fe6a6a3dd..bbc7bd9b9961 100755 --- a/test/functional/feature_dip3_deterministicmns.py +++ b/test/functional/feature_dip3_deterministicmns.py @@ -243,7 +243,7 @@ def register_fund_mn(self, node, mn): mn.collateral_address = node.getnewaddress() mn.rewards_address = node.getnewaddress() - mn.protx_hash = node.protx('register_fund', mn.collateral_address, 1000, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) + mn.protx_hash = node.protx('register_fund', mn.collateral_address, '127.0.0.1:%d' % mn.p2p_port, mn.ownerAddr, mn.operatorAddr, mn.votingAddr, 0, mn.rewards_address, mn.fundsAddr) mn.collateral_txid = mn.protx_hash mn.collateral_vout = None diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index 86f60cdb7291..c2bb706107f3 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -10,7 +10,9 @@ ''' from _decimal import Decimal +import random +from test_framework.script import hash160 from test_framework.test_framework import DashTestFramework from test_framework.util import ( assert_equal, p2p_port @@ -62,23 +64,26 @@ def run_test(self): (quorum_info_i_0, quorum_info_i_1) = self.mine_cycle_quorum(llmq_type_name='llmq_test_dip0024', llmq_type=103) hpmn_protxhash_list = list() - for i in range(6): - protx = self.dynamically_add_masternode(hpmn=True) - hpmn_protxhash_list.append(protx) + for i in range(5): + hpmn_info = self.dynamically_add_masternode(hpmn=True) + hpmn_protxhash_list.append(hpmn_info.proTxHash) self.nodes[0].generate(8) self.sync_blocks(self.nodes) + self.test_hpmn_update_service(hpmn_info) - self.log.info("Test llmq_platform are formed only with HPMN") + self.log.info("Test llmq_platform are formed only with HPMNs") for i in range(3): quorum_i_hash = self.mine_quorum(llmq_type_name='llmq_test_platform', llmq_type=106) self.test_quorum_members_are_high_performance(quorum_i_hash, llmq_type=106) - self.log.info("Test that HPMN are present in MN list") + self.log.info("Test that HPMNs are present in MN list") self.test_hpmn_protx_are_in_mnlist(hpmn_protxhash_list) self.log.info("Test that HPMNs are paid 4x blocks in a row") self.test_hpmmn_payements(window_analysis=256) + self.log.info(self.nodes[0].masternodelist()) + return def test_hpmmn_payements(self, window_analysis): @@ -170,12 +175,30 @@ def test_hpmn_is_rejected_before_v19(self): protx_success = False try: - self.nodes[0].protx('register', collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True) + self.nodes[0].protx('register_hpmn', collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True) protx_success = True except: self.log.info("protx_hpmn rejected") assert_equal(protx_success, False) + def test_hpmn_update_service(self, hpmn_info): + funds_address = self.nodes[0].getnewaddress() + operator_reward_address = self.nodes[0].getnewaddress() + + # For the sake of the test, generate random nodeid, p2p and http platform values + rnd = random.randint(1000, 65000) + platform_node_id = hash160(b'%d' % rnd).hex() + platform_p2p_port = '%d' % (rnd + 1) + platform_http_port = '%d' % (rnd + 2) + + self.nodes[0].sendtoaddress(funds_address, 1) + self.nodes[0].generate(1) + self.sync_all(self.nodes) + + self.nodes[0].protx('update_service_hpmn', hpmn_info.proTxHash, hpmn_info.addr, hpmn_info.keyOperator, platform_node_id, platform_p2p_port, platform_http_port, operator_reward_address, funds_address) + self.nodes[0].generate(1) + self.sync_all(self.nodes) + self.log.info("Updated HPMN %s: platformNodeID=%s, platformP2PPort=%s, platformHTTPPort=%s" % (hpmn_info.proTxHash, platform_node_id, platform_p2p_port, platform_http_port)) if __name__ == '__main__': LLMQHPMNTest().main() diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 486c469a14a6..ae8776460cea 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -899,7 +899,7 @@ def is_zmq_compiled(self): HIGHPERFORMANCE_MASTERNODE_COLLATERAL = 4000 class MasternodeInfo: - def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout, hpmn=False): + def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator, collateral_address, collateral_txid, collateral_vout, addr, hpmn=False): self.proTxHash = proTxHash self.ownerAddr = ownerAddr self.votingAddr = votingAddr @@ -908,6 +908,7 @@ def __init__(self, proTxHash, ownerAddr, votingAddr, pubKeyOperator, keyOperator self.collateral_address = collateral_address self.collateral_txid = collateral_txid self.collateral_vout = collateral_vout + self.addr = addr self.hpmn = hpmn @@ -1051,7 +1052,7 @@ def dynamically_add_masternode(self, hpmn=False): force_finish_mnsync(self.nodes[mn_idx]) self.log.info("Successfully started and synced proTx:"+str(created_mn_info.proTxHash)) - return created_mn_info.proTxHash + return created_mn_info def dynamically_prepare_masternode(self, idx, node_p2p_port, hpmn=False): bls = self.nodes[0].bls('generate') @@ -1061,6 +1062,9 @@ def dynamically_prepare_masternode(self, idx, node_p2p_port, hpmn=False): voting_address = self.nodes[0].getnewaddress() reward_address = self.nodes[0].getnewaddress() + platform_p2p_port = '%d' % (node_p2p_port + 101) if hpmn else '' + platform_http_port = '%d' % (node_p2p_port + 102) if hpmn else '' + collateral_amount = 4000 if hpmn else 1000 collateral_txid = self.nodes[0].sendtoaddress(collateral_address, collateral_amount) # send to same address to reserve some funds for fees @@ -1080,10 +1084,11 @@ def dynamically_prepare_masternode(self, idx, node_p2p_port, hpmn=False): operatorReward = idx self.nodes[0].generate(1) - protx_result = self.nodes[0].protx('register', collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, funds_address, True) + register_rpc = 'register_hpmn' if hpmn else 'register' + protx_result = self.nodes[0].protx(register_rpc, collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, '', platform_p2p_port, platform_http_port, funds_address, True) self.nodes[0].generate(1) self.sync_all(self.nodes) - mn_info = MasternodeInfo(protx_result, owner_address, voting_address, bls['public'], bls['secret'], collateral_address, collateral_txid, collateral_vout, hpmn) + mn_info = MasternodeInfo(protx_result, owner_address, voting_address, bls['public'], bls['secret'], collateral_address, collateral_txid, collateral_vout, ipAndPort, hpmn) self.mninfo.append(mn_info) mn_type_str = "HPMN" if hpmn else "MN" @@ -1135,8 +1140,7 @@ def prepare_masternode(self, idx, rewardsAddr=None, hpmn=False): if register_fund: # self.nodes[0].lockunspent(True, [{'txid': txid, 'vout': collateral_vout}]) - requested_fund_amout = 4000 if hpmn else 1000 - protx_result = self.nodes[0].protx('register_fund', address, requested_fund_amout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) + protx_result = self.nodes[0].protx('register_fund', address, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) else: self.nodes[0].generate(1) protx_result = self.nodes[0].protx('register', txid, collateral_vout, ipAndPort, ownerAddr, bls['public'], votingAddr, operatorReward, rewardsAddr, address, submit) @@ -1151,7 +1155,7 @@ def prepare_masternode(self, idx, rewardsAddr=None, hpmn=False): operatorPayoutAddress = self.nodes[0].getnewaddress() self.nodes[0].protx('update_service', proTxHash, ipAndPort, bls['secret'], operatorPayoutAddress, address) - self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout, hpmn)) + self.mninfo.append(MasternodeInfo(proTxHash, ownerAddr, votingAddr, bls['public'], bls['secret'], address, txid, collateral_vout, ipAndPort, hpmn)) # self.sync_all() mn_type_str = "HPMN" if hpmn else "MN" From 5323b5c2c986a5109f860ff3204fe246ac51bf89 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 26 Jan 2023 11:41:31 +0200 Subject: [PATCH 23/64] Added release-notes --- doc/release-notes-5039.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 doc/release-notes-5039.md diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md new file mode 100644 index 000000000000..ce0db01ae650 --- /dev/null +++ b/doc/release-notes-5039.md @@ -0,0 +1,13 @@ +Added RPCs +-------- + +The following RPCs were added `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. +Those RPCs are the corresponding versions for HPMNs and they have the following mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformNodeID`. + +- `platformNodeID`: Platform P2P node ID, derived from P2P public key. +- `platformP2PPort`: TCP port of Dash Platform peer-to-peer communication between nodes (network byte order). +- `platformNodeID`: TCP port of Platform HTTP/API interface (network byte order). + +Notes: +- Since the Platform Node ID can not be known at this time, `platformNodeID` can be null. +- `platformP2PPort`, `platformNodeID` and the Core port must be distinct. \ No newline at end of file From b48dbc7cf7fc8422f262ad0f127122441cb94f51 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 26 Jan 2023 12:39:51 +0200 Subject: [PATCH 24/64] Removed empty lines --- doc/release-notes-5039.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index ce0db01ae650..eda9121f9e94 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -3,11 +3,9 @@ Added RPCs The following RPCs were added `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. Those RPCs are the corresponding versions for HPMNs and they have the following mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformNodeID`. - - `platformNodeID`: Platform P2P node ID, derived from P2P public key. - `platformP2PPort`: TCP port of Dash Platform peer-to-peer communication between nodes (network byte order). - `platformNodeID`: TCP port of Platform HTTP/API interface (network byte order). - Notes: - Since the Platform Node ID can not be known at this time, `platformNodeID` can be null. - `platformP2PPort`, `platformNodeID` and the Core port must be distinct. \ No newline at end of file From e5b3668d895800eff34d6edda0af735eaf01a8c3 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 26 Jan 2023 13:01:08 +0200 Subject: [PATCH 25/64] linter fix --- doc/release-notes-5039.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index eda9121f9e94..194a5964691d 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -1,7 +1,7 @@ Added RPCs -------- -The following RPCs were added `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. +The following RPCs were added `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. Those RPCs are the corresponding versions for HPMNs and they have the following mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformNodeID`. - `platformNodeID`: Platform P2P node ID, derived from P2P public key. - `platformP2PPort`: TCP port of Dash Platform peer-to-peer communication between nodes (network byte order). From 10e838b3cb5cd9b9b0065719cc3635104cf083f9 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 26 Jan 2023 16:46:41 +0200 Subject: [PATCH 26/64] correct migration of CDeterministicMNState --- src/evo/deterministicmns.h | 15 +++++++-- src/evo/dmnstate.h | 68 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 2663fbd05f70..015c2fdb4271 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -73,9 +73,18 @@ class CDeterministicMN READWRITE(VARINT(internalId)); READWRITE(collateralOutpoint); READWRITE(nOperatorReward); - READWRITE(pdmnState); - // We need to serialise nType if: - // format_version is set to MN_TYPE_FORMAT (For Serialisation it is always the case) Needed for the MNLISTDIFF Migration in evoDB + // We need to read CDeterministicMNState using the old format only when called with CURRENT_MN_FORMAT on Unserialize() + // Serialisation (writing) will be done always using new format + if (ser_action.ForRead() && format_version == CURRENT_MN_FORMAT) { + CDeterministicMNState_Oldformat old_state; + READWRITE(old_state); + pdmnState = std::make_shared(old_state); + } + else { + READWRITE(pdmnState); + } + // We need to read/write nType if: + // format_version is set to MN_TYPE_FORMAT (For writing (serialisation) it is always the case) Needed for the MNLISTDIFF Migration in evoDB // We can't know if we are serialising for the Disk or for the Network here (s.GetType() is not accessible) // Therefore if s.GetVersion() == CLIENT_VERSION -> Then we know we are serialising for the Disk // Otherwise, we can safely check with protocol versioning logic so we won't break old clients diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index c31d3e83bb39..b6035d5c16bd 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -25,13 +25,59 @@ namespace llmq class CFinalCommitment; } // namespace llmq -class CDeterministicMNState +//TODO: To remove this in the future +class CDeterministicMNState_Oldformat { private: int nPoSeBanHeight{-1}; friend class CDeterministicMNStateDiff; + friend class CDeterministicMNState; +public: + int nRegisteredHeight{-1}; + int nLastPaidHeight{0}; + int nPoSePenalty{0}; + int nPoSeRevivedHeight{-1}; + uint16_t nRevocationReason{CProUpRevTx::REASON_NOT_SPECIFIED}; + uint256 confirmedHash; + uint256 confirmedHashWithProRegTxHash; + CKeyID keyIDOwner; + CBLSLazyPublicKey pubKeyOperator; + CKeyID keyIDVoting; + CService addr; + CScript scriptPayout; + CScript scriptOperatorPayout; + +public: + CDeterministicMNState_Oldformat() = default; + + SERIALIZE_METHODS(CDeterministicMNState_Oldformat, obj) + { + READWRITE( + obj.nRegisteredHeight, + obj.nLastPaidHeight, + obj.nPoSePenalty, + obj.nPoSeRevivedHeight, + obj.nPoSeBanHeight, + obj.nRevocationReason, + obj.confirmedHash, + obj.confirmedHashWithProRegTxHash, + obj.keyIDOwner, + obj.pubKeyOperator, + obj.keyIDVoting, + obj.addr, + obj.scriptPayout, + obj.scriptOperatorPayout + ); + } +}; + +class CDeterministicMNState +{ +private: + int nPoSeBanHeight{-1}; + friend class CDeterministicMNStateDiff; public: int nRegisteredHeight{-1}; int nLastPaidHeight{0}; @@ -70,6 +116,25 @@ class CDeterministicMNState { pubKeyOperator.Set(proTx.pubKeyOperator); } + explicit CDeterministicMNState(const CDeterministicMNState_Oldformat& s) : + nPoSeBanHeight(s.nPoSeBanHeight), + nRegisteredHeight(s.nRegisteredHeight), + nLastPaidHeight(s.nLastPaidHeight), + nConsecutivePayments(0), + nPoSePenalty(s.nPoSePenalty), + nPoSeRevivedHeight(s.nPoSeRevivedHeight), + nRevocationReason(s.nRevocationReason), + confirmedHash(s.confirmedHash), + confirmedHashWithProRegTxHash(s.confirmedHashWithProRegTxHash), + keyIDOwner(s.keyIDOwner), + pubKeyOperator(s.pubKeyOperator), + keyIDVoting(s.keyIDVoting), + addr(s.addr), + scriptPayout(s.scriptPayout), + scriptOperatorPayout(s.scriptOperatorPayout), + platformNodeID(), + platformP2PPort(0), + platformHTTPPort(0){} template CDeterministicMNState(deserialize_type, Stream& s) { @@ -220,5 +285,4 @@ class CDeterministicMNStateDiff } }; - #endif //BITCOIN_EVO_DMNSTATE_H From 9f0a794becaa9967050450511ac4fb8b46118d30 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Fri, 27 Jan 2023 16:01:01 +0200 Subject: [PATCH 27/64] Updated default Platform ports --- src/chainparams.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 4fb54ea35382..93c6b05e97bf 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -271,9 +271,8 @@ class CMainParams : public CChainParams { pchMessageStart[2] = 0x6b; pchMessageStart[3] = 0xbd; nDefaultPort = 9999; - //TODO: Determine default values - nDefaultPlatformP2PPort = 22000; - nDefaultPlatformHTTPPort = 22001; + nDefaultPlatformP2PPort = 26656; + nDefaultPlatformHTTPPort = 443; nPruneAfterHeight = 100000; m_assumed_blockchain_size = 45; m_assumed_chain_state_size = 1; @@ -505,9 +504,8 @@ class CTestNetParams : public CChainParams { pchMessageStart[2] = 0xca; pchMessageStart[3] = 0xff; nDefaultPort = 19999; - //TODO: Determine default values - nDefaultPlatformP2PPort = 23000; - nDefaultPlatformHTTPPort = 23001; + nDefaultPlatformP2PPort = 22000; + nDefaultPlatformHTTPPort = 22001; nPruneAfterHeight = 1000; m_assumed_blockchain_size = 4; m_assumed_chain_state_size = 1; @@ -716,9 +714,8 @@ class CDevNetParams : public CChainParams { pchMessageStart[2] = 0xff; pchMessageStart[3] = 0xce; nDefaultPort = 19799; - //TODO: Determine default values - nDefaultPlatformP2PPort = 24000; - nDefaultPlatformHTTPPort = 24001; + nDefaultPlatformP2PPort = 22100; + nDefaultPlatformHTTPPort = 22101; nPruneAfterHeight = 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; @@ -969,9 +966,8 @@ class CRegTestParams : public CChainParams { pchMessageStart[2] = 0xb7; pchMessageStart[3] = 0xdc; nDefaultPort = 19899; - //TODO: Determine default values - nDefaultPlatformP2PPort = 25000; - nDefaultPlatformHTTPPort = 25001; + nDefaultPlatformP2PPort = 22200; + nDefaultPlatformHTTPPort = 22201; nPruneAfterHeight = 1000; m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; From f454c832074e01991cdf677f4ee1f670ead9fb19 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 1 Feb 2023 12:05:50 +0200 Subject: [PATCH 28/64] refactoring --- doc/release-notes-5039.md | 8 ++++---- src/evo/dmnstate.h | 2 +- src/rpc/evo.cpp | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index 194a5964691d..d45cf72fe560 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -2,10 +2,10 @@ Added RPCs -------- The following RPCs were added `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. -Those RPCs are the corresponding versions for HPMNs and they have the following mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformNodeID`. +These HPMN RPCs correspond to the standard masternode RPCs but have the following additional mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformHTTPPort`. - `platformNodeID`: Platform P2P node ID, derived from P2P public key. - `platformP2PPort`: TCP port of Dash Platform peer-to-peer communication between nodes (network byte order). -- `platformNodeID`: TCP port of Platform HTTP/API interface (network byte order). +- `platformHTTPPort`: TCP port of Platform HTTP/API interface (network byte order). Notes: -- Since the Platform Node ID can not be known at this time, `platformNodeID` can be null. -- `platformP2PPort`, `platformNodeID` and the Core port must be distinct. \ No newline at end of file +- Since the Platform Node ID cannot be known at this time, `platformNodeID` can be null. +- `platformP2PPort`, `platformHTTPPort` and the Core port must be distinct. \ No newline at end of file diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index b6035d5c16bd..1cc4f0c3a3ac 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -233,7 +233,7 @@ class CDeterministicMNStateDiff #define DMN_STATE_DIFF_ALL_FIELDS \ DMN_STATE_DIFF_LINE(nRegisteredHeight) \ - DMN_STATE_DIFF_LINE(nLastPaidHeight) \ + DMN_STATE_DIFF_LINE(nLastPaidHeight) \ DMN_STATE_DIFF_LINE(nPoSePenalty) \ DMN_STATE_DIFF_LINE(nPoSeRevivedHeight) \ DMN_STATE_DIFF_LINE(nPoSeBanHeight) \ diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 246f94199b07..b7420d03e47c 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -367,7 +367,7 @@ static void protx_register_fund_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } @@ -459,7 +459,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, const bool isFundRegister, const bool isPrepareRegister) { - if (isFundRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { + if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { protx_register_fund_help(request); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { protx_register_help(request); @@ -1087,7 +1087,7 @@ static void protx_update_service_hpmn_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "txid", "The transaction id" }, RPCExamples{ - HelpExampleCli("protx", "update_service \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" 5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd") + HelpExampleCli("protx", "update_service_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" \"5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") }, }.Check(request); } From 1313a5b9bf1d446c0fa3df076549392d71c7e90d Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 1 Feb 2023 12:32:33 +0200 Subject: [PATCH 29/64] platformNodeID uniqueness --- src/evo/deterministicmns.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index c601bde66650..8e14236f8e7e 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -470,6 +470,14 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && !dmn->pdmnState->platformNodeID.IsNull()) { + if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { + mnUniquePropertyMap = mnUniquePropertyMapSaved; + throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate platformNodeID=%s", __func__, + dmn->proTxHash.ToString(), dmn->pdmnState->platformNodeID.ToString()))); + } + } + mnMap = mnMap.set(dmn->proTxHash, dmn); mnInternalIdMap = mnInternalIdMap.set(dmn->GetInternalId(), dmn->proTxHash); if (fBumpTotalCount) { @@ -503,6 +511,13 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate pubKeyOperator=%s", __func__, oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.Get().ToString()))); } + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && !dmn->pdmnState->platformNodeID.IsNull()) { + if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) { + mnUniquePropertyMap = mnUniquePropertyMapSaved; + throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate platformNodeID=%s", __func__, + dmn->proTxHash.ToString(), dmn->pdmnState->platformNodeID.ToString()))); + } + } mnMap = mnMap.set(oldDmn.proTxHash, dmn); } @@ -556,6 +571,14 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && !dmn->pdmnState->platformNodeID.IsNull()) { + if (!DeleteUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { + mnUniquePropertyMap = mnUniquePropertyMapSaved; + throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a duplicate platformNodeID=%s", __func__, + dmn->proTxHash.ToString(), dmn->pdmnState->platformNodeID.ToString()))); + } + } + mnMap = mnMap.erase(proTxHash); mnInternalIdMap = mnInternalIdMap.erase(dmn->GetInternalId()); } From 82b77763b07eb10437967fdc128be27d521372e9 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 1 Feb 2023 12:41:30 +0200 Subject: [PATCH 30/64] release notes fix --- doc/release-notes-5039.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index d45cf72fe560..e117974aad11 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -1,7 +1,7 @@ Added RPCs -------- -The following RPCs were added `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. +The following RPCs were added: `protx register_hpmn`, `protx register_fund_hpmn`, `protx register_prepare_hpmn` and `protx update_service_hpmn`. These HPMN RPCs correspond to the standard masternode RPCs but have the following additional mandatory arguments: `platformNodeID`, `platformP2PPort` and `platformHTTPPort`. - `platformNodeID`: Platform P2P node ID, derived from P2P public key. - `platformP2PPort`: TCP port of Dash Platform peer-to-peer communication between nodes (network byte order). From 291a62021e7b305d5f4f3c0cf4358ca63329696d Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 1 Feb 2023 15:12:21 +0200 Subject: [PATCH 31/64] platformNodeID null not allowed --- doc/release-notes-5039.md | 2 +- src/evo/deterministicmns.cpp | 15 ++++++------- src/rpc/evo.cpp | 22 +++++-------------- .../test_framework/test_framework.py | 4 +++- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index e117974aad11..91652e981b2f 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -7,5 +7,5 @@ These HPMN RPCs correspond to the standard masternode RPCs but have the followin - `platformP2PPort`: TCP port of Dash Platform peer-to-peer communication between nodes (network byte order). - `platformHTTPPort`: TCP port of Platform HTTP/API interface (network byte order). Notes: -- Since the Platform Node ID cannot be known at this time, `platformNodeID` can be null. +- `platformNodeID` must be unique across the network. - `platformP2PPort`, `platformHTTPPort` and the Core port must be distinct. \ No newline at end of file diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 8e14236f8e7e..5dffa97cc0af 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -470,7 +470,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && !dmn->pdmnState->platformNodeID.IsNull()) { + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -511,7 +511,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate pubKeyOperator=%s", __func__, oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && !dmn->pdmnState->platformNodeID.IsNull()) { + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -571,7 +571,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && !dmn->pdmnState->platformNodeID.IsNull()) { + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { if (!DeleteUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -1288,10 +1288,9 @@ static bool CheckService(const ProTx& proTx, CValidationState& state) } template -static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state, bool allowNullValues) +static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state) { - // platformNodeID can be null since it can not be known at this time (v19 HPMNs registration) - if (!allowNullValues && proTx.platformNodeID.IsNull()) { + if (proTx.platformNodeID.IsNull()) { return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-nodeid"); } @@ -1374,7 +1373,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid } if (ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { - if (!CheckPlatformFields(ptx, state, true)) { + if (!CheckPlatformFields(ptx, state)) { return false; } } @@ -1485,7 +1484,7 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa } if (ptx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { - if (!CheckPlatformFields(ptx, state, true)) { + if (!CheckPlatformFields(ptx, state)) { return false; } } diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index b7420d03e47c..e43bcbda24e3 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -813,15 +813,10 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); } - // platformNodeID can be null in ProRegTx since it can not be known at this time (v19 HPMNs registration) - if (request.params[paramIdx + 6].get_str().empty()) { - ptx.platformNodeID.SetNull(); - } - else { - if (!IsHex(request.params[paramIdx + 6].get_str())) - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); - ptx.platformNodeID.SetHex(request.params[paramIdx + 6].get_str()); + if (!IsHex(request.params[paramIdx + 6].get_str())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); } + ptx.platformNodeID.SetHex(request.params[paramIdx + 6].get_str()); int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 7].get_str(), "platformP2PPort"); if (!ValidatePort(requestedPlatformP2PPort)) { @@ -1116,15 +1111,10 @@ static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) CBLSSecretKey keyOperator = ParseBLSSecretKey(request.params[2].get_str(), "operatorKey"); - // platformNodeID can be null since it can not be known at this time (v19 HPMNs registration) - if (request.params[3].get_str().empty()) { - ptx.platformNodeID.SetNull(); - } - else { - if (!IsHex(request.params[3].get_str())) - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); - ptx.platformNodeID.SetHex(request.params[3].get_str()); + if (!IsHex(request.params[3].get_str())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); } + ptx.platformNodeID.SetHex(request.params[3].get_str()); int32_t requestedPlatformP2PPort = ParseInt32V(request.params[4].get_str(), "platformP2PPort"); if (!ValidatePort(requestedPlatformP2PPort)) { diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index ae8776460cea..cf4826b75484 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -33,6 +33,7 @@ ser_compact_size, ser_string, ) +from .script import hash160 from .test_node import TestNode from .mininode import NetworkThread from .util import ( @@ -1062,6 +1063,7 @@ def dynamically_prepare_masternode(self, idx, node_p2p_port, hpmn=False): voting_address = self.nodes[0].getnewaddress() reward_address = self.nodes[0].getnewaddress() + platform_node_id = hash160(b'%d' % node_p2p_port).hex() platform_p2p_port = '%d' % (node_p2p_port + 101) if hpmn else '' platform_http_port = '%d' % (node_p2p_port + 102) if hpmn else '' @@ -1085,7 +1087,7 @@ def dynamically_prepare_masternode(self, idx, node_p2p_port, hpmn=False): self.nodes[0].generate(1) register_rpc = 'register_hpmn' if hpmn else 'register' - protx_result = self.nodes[0].protx(register_rpc, collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, '', platform_p2p_port, platform_http_port, funds_address, True) + protx_result = self.nodes[0].protx(register_rpc, collateral_txid, collateral_vout, ipAndPort, owner_address, bls['public'], voting_address, operatorReward, reward_address, platform_node_id, platform_p2p_port, platform_http_port, funds_address, True) self.nodes[0].generate(1) self.sync_all(self.nodes) mn_info = MasternodeInfo(protx_result, owner_address, voting_address, bls['public'], bls['secret'], collateral_address, collateral_txid, collateral_vout, ipAndPort, hpmn) From ff46220198ad1af4c755b1415412c0da72cf4a6d Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 1 Feb 2023 15:17:07 +0200 Subject: [PATCH 32/64] tab fixes --- src/rpc/evo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index e43bcbda24e3..aae11b25b816 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -367,7 +367,7 @@ static void protx_register_fund_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", "register_fund \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } @@ -398,7 +398,7 @@ static void protx_register_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, }, RPCExamples{ - HelpExampleCli("protx", "register \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", "register \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } @@ -428,7 +428,7 @@ static void protx_register_prepare_help(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, }}, RPCExamples{ - HelpExampleCli("protx", "register_prepare \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") + HelpExampleCli("protx", "register_prepare \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\"") }, }.Check(request); } @@ -496,6 +496,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); } CScript collateralScript = GetScriptForDestination(collateralDest); + CTxOut collateralTxOut(collateralAmount, collateralScript); tx.vout.emplace_back(collateralTxOut); From 321d7d9dc9bd8159e0db9f509aa6be91dba0b1c7 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 2 Feb 2023 15:43:47 +0200 Subject: [PATCH 33/64] Added voteWight in getcurrentvotes --- doc/release-notes-5039.md | 9 ++++++++- src/governance/vote.cpp | 11 ++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index 91652e981b2f..621439fa1d5a 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -8,4 +8,11 @@ These HPMN RPCs correspond to the standard masternode RPCs but have the followin - `platformHTTPPort`: TCP port of Platform HTTP/API interface (network byte order). Notes: - `platformNodeID` must be unique across the network. -- `platformP2PPort`, `platformHTTPPort` and the Core port must be distinct. \ No newline at end of file +- `platformP2PPort`, `platformHTTPPort` and the Core port must be distinct. + + +Updated RPCs +-------- + +The RPC's `gobject getcurrentvotes` reply is enriched with the weight of the vote. Possible values 1 or 4. Example: +`"7cb20c883c6093b8489f795b3ec0aad0d9c2c2821610ae9ed938baaf42fec66d": "277e6345359071410ab691c21a3a16f8f46c9229c2f8ec8f028c9a95c0f1c0e7-1:1670019339:yes:funding:4"` \ No newline at end of file diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 516acbedabf6..a0ccc44a554b 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -110,11 +110,20 @@ CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const ui std::string CGovernanceVote::ToString() const { + Coin coin; + int voteWeight = 1; + CAmount HPMNCollateralAmount = 4000 * COIN; + if (!GetUTXOCoin(masternodeOutpoint, coin)) { + if (coin.out.nValue == HPMNCollateralAmount) { + voteWeight = 4; + } + } std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" << nTime << ":" << CGovernanceVoting::ConvertOutcomeToString(GetOutcome()) << ":" - << CGovernanceVoting::ConvertSignalToString(GetSignal()); + << CGovernanceVoting::ConvertSignalToString(GetSignal()) << ":" + << voteWeight; return ostr.str(); } From 69a259eb55e724965fa9e08f9d108e55de86314b Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 2 Feb 2023 15:53:44 +0200 Subject: [PATCH 34/64] refactoring --- src/governance/object.cpp | 12 +++++------- src/governance/vote.cpp | 8 +++----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 861bf0708a54..ab5a43f58ce0 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -621,6 +621,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote { LOCK(cs); + static CAmount HPMNCollateralAmount = 4000 * COIN; int nCount = 0; for (const auto& votepair : mapCurrentMNVotes) { const vote_rec_t& recVote = votepair.second; @@ -628,13 +629,10 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote if (it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { Coin coin; int voteWeight = 1; - if (!GetUTXOCoin(votepair.first, coin)) { - // 4x times weight vote for HPMN owners. - // No need to check if v19 is active since no HPMN are allowed to register before v19 - CAmount HPMNCollateralAmount = 4000 * COIN; - if (coin.out.nValue == HPMNCollateralAmount) { - voteWeight = 4; - } + // 4x times weight vote for HPMN owners. + // No need to check if v19 is active since no HPMN are allowed to register before v19s + if (GetUTXOCoin(votepair.first, coin) && coin.out.nValue == HPMNCollateralAmount) { + voteWeight = 4; } nCount += voteWeight; } diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index a0ccc44a554b..5931b4d6b999 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -112,11 +112,9 @@ std::string CGovernanceVote::ToString() const { Coin coin; int voteWeight = 1; - CAmount HPMNCollateralAmount = 4000 * COIN; - if (!GetUTXOCoin(masternodeOutpoint, coin)) { - if (coin.out.nValue == HPMNCollateralAmount) { - voteWeight = 4; - } + static CAmount HPMNCollateralAmount = 4000 * COIN; + if (GetUTXOCoin(masternodeOutpoint, coin) && coin.out.nValue == HPMNCollateralAmount) { + voteWeight = 4; } std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" From 6155dee1479d99f60458f4744642e108cf300bcc Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Thu, 2 Feb 2023 16:19:15 +0200 Subject: [PATCH 35/64] Update doc/release-notes-5039.md Co-authored-by: thephez --- doc/release-notes-5039.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index 621439fa1d5a..a0c5b5c5dc5f 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -14,5 +14,5 @@ Notes: Updated RPCs -------- -The RPC's `gobject getcurrentvotes` reply is enriched with the weight of the vote. Possible values 1 or 4. Example: +The RPC's `gobject getcurrentvotes` reply is enriched by adding the vote weight at the end of each line. Possible values are 1 or 4. Example: `"7cb20c883c6093b8489f795b3ec0aad0d9c2c2821610ae9ed938baaf42fec66d": "277e6345359071410ab691c21a3a16f8f46c9229c2f8ec8f028c9a95c0f1c0e7-1:1670019339:yes:funding:4"` \ No newline at end of file From 316a0c6ade8b2bf9217ee7e1fccce03b26792b38 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Fri, 3 Feb 2023 10:00:46 +0200 Subject: [PATCH 36/64] use constexpr for collaterals and weight --- src/evo/deterministicmns.cpp | 12 +++++++++--- src/evo/deterministicmns.h | 4 ++++ src/rpc/evo.cpp | 10 ++++------ src/test/block_reward_reallocation_tests.cpp | 2 +- src/test/evo_deterministicmns_tests.cpp | 18 +++++++++--------- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 5dffa97cc0af..4563fe21da53 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -759,7 +759,9 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } Coin coin; - CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE ? 4000 * COIN : 1000 * COIN; + CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL + : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral)) { // should actually never get to this point as CheckProRegTx should have handled this case. // We do this additional check nevertheless to be 100% sure @@ -1093,7 +1095,9 @@ bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, u return false; } - CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE ? 4000 * COIN : 1000 * COIN; + CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL + : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; if (tx->vout[n].nValue != expectedCollateral) { return false; @@ -1382,7 +1386,9 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid const PKHash *keyForPayloadSig = nullptr; COutPoint collateralOutpoint; - CAmount expectedCollateral = ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE ? 4000 * COIN : 1000 * COIN; + CAmount expectedCollateral = ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL + : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; if (!ptx.collateralOutpoint.hash.IsNull()) { Coin coin; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 015c2fdb4271..8ef7d1e32e8b 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -43,6 +43,10 @@ class CDeterministicMN static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; + static constexpr CAmount REGULAR_MASTERNODE_COLLATERAL{1000 * COIN}; + static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_WEIGHT{4}; + static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_COLLATERAL{REGULAR_MASTERNODE_COLLATERAL * CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT}; + static constexpr uint16_t CURRENT_MN_FORMAT = 0; static constexpr uint16_t MN_TYPE_FORMAT = 1; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index aae11b25b816..422c2f5b89a1 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -489,7 +489,6 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, ptx.nVersion = CProRegTx::GetVersion(isV19active); ptx.nType = CProRegTx::TYPE_REGULAR_MASTERNODE; - CAmount collateralAmount = 1000 * COIN; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); if (!IsValidDestination(collateralDest)) { @@ -497,7 +496,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, } CScript collateralScript = GetScriptForDestination(collateralDest); - CTxOut collateralTxOut(collateralAmount, collateralScript); + CTxOut collateralTxOut(CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, collateralScript); tx.vout.emplace_back(collateralTxOut); paramIdx++; @@ -571,7 +570,7 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, if (isFundRegister) { uint32_t collateralIndex = (uint32_t) -1; for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == collateralAmount) { + if (tx.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { collateralIndex = i; break; } @@ -760,14 +759,13 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, ptx.nVersion = CProRegTx::GetVersion(isV19active); ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; - CAmount requestedCollateralAmount = 4000 * COIN; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); if (!IsValidDestination(collateralDest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); } CScript collateralScript = GetScriptForDestination(collateralDest); - CTxOut collateralTxOut(requestedCollateralAmount, collateralScript); + CTxOut collateralTxOut(CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL, collateralScript); tx.vout.emplace_back(collateralTxOut); paramIdx++; @@ -858,7 +856,7 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, if (isFundRegister) { uint32_t collateralIndex = (uint32_t) -1; for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == requestedCollateralAmount) { + if (tx.vout[i].nValue == CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL) { collateralIndex = i; break; } diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp index bb19c25c741b..6f32f1dcde02 100644 --- a/src/test/block_reward_reallocation_tests.cpp +++ b/src/test/block_reward_reallocation_tests.cpp @@ -132,7 +132,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM CMutableTransaction tx; tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx, utxos, scriptPayout, 1000 * COIN); + FundTransaction(tx, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL); proTx.inputsHash = CalcTxInputsHash(CTransaction(tx)); SetTxPayload(tx, proTx); SignTransaction(mempool, tx, coinbaseKey); diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index 62dd6cf992c7..f48fb65787e5 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -110,7 +110,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM CMutableTransaction tx; tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx, utxos, scriptPayout, 1000 * COIN, coinbaseKey); + FundTransaction(tx, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, coinbaseKey); proTx.inputsHash = CalcTxInputsHash(CTransaction(tx)); SetTxPayload(tx, proTx); SignTransaction(mempool, tx, coinbaseKey); @@ -468,7 +468,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) // Create a MN with an external collateral CMutableTransaction tx_collateral; - FundTransaction(tx_collateral, utxos, scriptCollateral, 1000 * COIN, setup.coinbaseKey); + FundTransaction(tx_collateral, utxos, scriptCollateral, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); SignTransaction(*(setup.m_node.mempool), tx_collateral, setup.coinbaseKey); auto block = std::make_shared(setup.CreateBlock({tx_collateral}, setup.coinbaseKey)); @@ -486,7 +486,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) payload.scriptPayout = scriptPayout; for (size_t i = 0; i < tx_collateral.vout.size(); ++i) { - if (tx_collateral.vout[i].nValue == 1000 * COIN) { + if (tx_collateral.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { payload.collateralOutpoint = COutPoint(tx_collateral.GetHash(), i); break; } @@ -495,7 +495,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) CMutableTransaction tx_reg; tx_reg.nVersion = 3; tx_reg.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx_reg, utxos, scriptPayout, 1000 * COIN, setup.coinbaseKey); + FundTransaction(tx_reg, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); payload.inputsHash = CalcTxInputsHash(CTransaction(tx_reg)); CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey); SetTxPayload(tx_reg, payload); @@ -555,7 +555,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) payload.scriptPayout = scriptPayout; for (size_t i = 0; i < tx_reg1.vout.size(); ++i) { - if (tx_reg1.vout[i].nValue == 1000 * COIN) { + if (tx_reg1.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { payload.collateralOutpoint = COutPoint(tx_reg1.GetHash(), i); break; } @@ -564,7 +564,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) CMutableTransaction tx_reg2; tx_reg2.nVersion = 3; tx_reg2.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx_reg2, utxos, scriptPayout, 1000 * COIN, setup.coinbaseKey); + FundTransaction(tx_reg2, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); payload.inputsHash = CalcTxInputsHash(CTransaction(tx_reg2)); CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey); SetTxPayload(tx_reg2, payload); @@ -599,7 +599,7 @@ void FuncVerifyDB(TestChainSetup& setup) // Create a MN with an external collateral CMutableTransaction tx_collateral; - FundTransaction(tx_collateral, utxos, scriptCollateral, 1000 * COIN, setup.coinbaseKey); + FundTransaction(tx_collateral, utxos, scriptCollateral, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); SignTransaction(*(setup.m_node.mempool), tx_collateral, setup.coinbaseKey); auto block = std::make_shared(setup.CreateBlock({tx_collateral}, setup.coinbaseKey)); @@ -617,7 +617,7 @@ void FuncVerifyDB(TestChainSetup& setup) payload.scriptPayout = scriptPayout; for (size_t i = 0; i < tx_collateral.vout.size(); ++i) { - if (tx_collateral.vout[i].nValue == 1000 * COIN) { + if (tx_collateral.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { payload.collateralOutpoint = COutPoint(tx_collateral.GetHash(), i); break; } @@ -626,7 +626,7 @@ void FuncVerifyDB(TestChainSetup& setup) CMutableTransaction tx_reg; tx_reg.nVersion = 3; tx_reg.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx_reg, utxos, scriptPayout, 1000 * COIN, setup.coinbaseKey); + FundTransaction(tx_reg, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); payload.inputsHash = CalcTxInputsHash(CTransaction(tx_reg)); CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey); SetTxPayload(tx_reg, payload); From 8fa0681d06da9753ad0c094a364cd8182c8e1d50 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Fri, 3 Feb 2023 10:05:55 +0200 Subject: [PATCH 37/64] use DMNL instead of UTXO in governance --- src/evo/deterministicmns.h | 1 + src/governance/object.cpp | 13 ++++++------- src/governance/vote.cpp | 11 +++++------ 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 8ef7d1e32e8b..83324e5f7038 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -43,6 +43,7 @@ class CDeterministicMN static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; + static constexpr CAmount REGULAR_MASTERNODE_WEIGHT{1}; static constexpr CAmount REGULAR_MASTERNODE_COLLATERAL{1000 * COIN}; static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_WEIGHT{4}; static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_COLLATERAL{REGULAR_MASTERNODE_COLLATERAL * CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT}; diff --git a/src/governance/object.cpp b/src/governance/object.cpp index ab5a43f58ce0..487c9f6d9656 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -619,22 +619,21 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote_outcome_enum_t eVoteOutcomeIn) const { + auto mnList = deterministicMNManager->GetListAtChainTip(); + LOCK(cs); - static CAmount HPMNCollateralAmount = 4000 * COIN; int nCount = 0; for (const auto& votepair : mapCurrentMNVotes) { const vote_rec_t& recVote = votepair.second; auto it2 = recVote.mapInstances.find(eVoteSignalIn); if (it2 != recVote.mapInstances.end() && it2->second.eOutcome == eVoteOutcomeIn) { - Coin coin; - int voteWeight = 1; // 4x times weight vote for HPMN owners. // No need to check if v19 is active since no HPMN are allowed to register before v19s - if (GetUTXOCoin(votepair.first, coin) && coin.out.nValue == HPMNCollateralAmount) { - voteWeight = 4; - } - nCount += voteWeight; + auto dmn = mnList.GetMNByCollateral(votepair.first); + nCount += (dmn != nullptr && dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT + : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; } } return nCount; diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 5931b4d6b999..2bb34167c772 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -110,12 +110,11 @@ CGovernanceVote::CGovernanceVote(const COutPoint& outpointMasternodeIn, const ui std::string CGovernanceVote::ToString() const { - Coin coin; - int voteWeight = 1; - static CAmount HPMNCollateralAmount = 4000 * COIN; - if (GetUTXOCoin(masternodeOutpoint, coin) && coin.out.nValue == HPMNCollateralAmount) { - voteWeight = 4; - } + auto mnList = deterministicMNManager->GetListAtChainTip(); + auto dmn = mnList.GetMNByCollateral(masternodeOutpoint); + int voteWeight = (dmn != nullptr && dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT + : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" << nTime << ":" From 068b9ec3f3bab8d047de403d7cac862f71cb4786 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 3 Feb 2023 00:21:49 +0300 Subject: [PATCH 38/64] make coin selection aware of HPMNs --- src/wallet/wallet.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 3544b7e639dd..e01a9585cad0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2535,7 +2535,7 @@ void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlySafe, const if (CCoinJoin::IsCollateralAmount(pcoin->tx->vout[i].nValue)) continue; // do not use collateral amounts found = !CCoinJoin::IsDenominatedAmount(pcoin->tx->vout[i].nValue); } else if(nCoinType == CoinType::ONLY_MASTERNODE_COLLATERAL) { - found = pcoin->tx->vout[i].nValue == 1000*COIN; + found = pcoin->tx->vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL || pcoin->tx->vout[i].nValue == CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL; } else if(nCoinType == CoinType::ONLY_COINJOIN_COLLATERAL) { found = CCoinJoin::IsCollateralAmount(pcoin->tx->vout[i].nValue); } else { @@ -3046,7 +3046,7 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD if(fAnonymizable) { // ignore collaterals if(CCoinJoin::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue; - if(fMasternodeMode && wtx.tx->vout[i].nValue == 1000*COIN) continue; + if (fMasternodeMode && (wtx.tx->vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL || wtx.tx->vout[i].nValue == CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL)) continue; // ignore outputs that are 10 times smaller then the smallest denomination // otherwise they will just lead to higher fee / lower priority if(wtx.tx->vout[i].nValue <= nSmallestDenom/10) continue; From 4f8e49b34aab4d02673e82322be28f093af0c076 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Fri, 3 Feb 2023 10:29:11 +0200 Subject: [PATCH 39/64] linter fix --- doc/release-notes-5039.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/release-notes-5039.md b/doc/release-notes-5039.md index a0c5b5c5dc5f..78b137b51089 100644 --- a/doc/release-notes-5039.md +++ b/doc/release-notes-5039.md @@ -14,5 +14,5 @@ Notes: Updated RPCs -------- -The RPC's `gobject getcurrentvotes` reply is enriched by adding the vote weight at the end of each line. Possible values are 1 or 4. Example: +The RPC's `gobject getcurrentvotes` reply is enriched by adding the vote weight at the end of each line. Possible values are 1 or 4. Example: `"7cb20c883c6093b8489f795b3ec0aad0d9c2c2821610ae9ed938baaf42fec66d": "277e6345359071410ab691c21a3a16f8f46c9229c2f8ec8f028c9a95c0f1c0e7-1:1670019339:yes:funding:4"` \ No newline at end of file From 1982db451e28ecb6e0ccf9d268ed4c410899e49f Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 18:48:04 +0200 Subject: [PATCH 40/64] check for v19 fork in GetMNPayee --- src/evo/deterministicmns.cpp | 34 +++++++++++++------------ src/evo/deterministicmns.h | 2 +- src/masternode/payments.cpp | 2 +- src/rpc/masternode.cpp | 6 ++--- src/test/evo_deterministicmns_tests.cpp | 6 ++--- 5 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 4563fe21da53..5b398f6fe8df 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -176,32 +176,34 @@ static bool CompareByLastPaid(const CDeterministicMN* _a, const CDeterministicMN return CompareByLastPaid(*_a, *_b); } -CDeterministicMNCPtr CDeterministicMNList::GetMNPayee() const +CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex) const { if (mnMap.size() == 0) { return nullptr; } + bool isv19Active = llmq::utils::IsV19Active(pIndex); // Starting from v19 and until v20 (Platform release), HPMN will be rewarded 4 blocks in a row - // No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation // TODO: Skip this code once v20 is active CDeterministicMNCPtr best = nullptr; - ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { - if (dmn->pdmnState->nLastPaidHeight == nHeight) { - // We found the last MN Payee. - // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if nConsecutivePayments < 3 - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->pdmnState->nConsecutivePayments < 3) { - best = dmn; + if (isv19Active) { + ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn->pdmnState->nLastPaidHeight == nHeight) { + // We found the last MN Payee. + // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if nConsecutivePayments < 3 + if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->pdmnState->nConsecutivePayments < 3) { + best = dmn; + } } - } - return; - }); + return; + }); - if (best) - return best; + if (best) + return best; - // Note: If the last payee was a regular MN or if the payee is a HPMN that was removed from the mnList then that's fine. - // We can proceed with classic MN payee selection + // Note: If the last payee was a regular MN or if the payee is a HPMN that was removed from the mnList then that's fine. + // We can proceed with classic MN payee selection + } ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { if (!best || CompareByLastPaid(dmn.get(), best.get())) { @@ -707,7 +709,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C newList.SetBlockHash(uint256()); // we can't know the final block hash, so better not return a (invalid) block hash newList.SetHeight(nHeight); - auto payee = oldList.GetMNPayee(); + auto payee = oldList.GetMNPayee(pindexPrev); // we iterate the oldList here and update the newList // this is only valid as long these have not diverged at this point, which is the case as long as we don't add diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 83324e5f7038..2fee288cdc2e 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -316,7 +316,7 @@ class CDeterministicMNList [[nodiscard]] CDeterministicMNCPtr GetValidMNByCollateral(const COutPoint& collateralOutpoint) const; [[nodiscard]] CDeterministicMNCPtr GetMNByService(const CService& service) const; [[nodiscard]] CDeterministicMNCPtr GetMNByInternalId(uint64_t internalId) const; - [[nodiscard]] CDeterministicMNCPtr GetMNPayee() const; + [[nodiscard]] CDeterministicMNCPtr GetMNPayee(const CBlockIndex* pIndex) const; /** * Calculates the projected MN payees for the next *count* blocks. The result is not guaranteed to be correct diff --git a/src/masternode/payments.cpp b/src/masternode/payments.cpp index e3ab3cc058ac..9e3fb01becd9 100644 --- a/src/masternode/payments.cpp +++ b/src/masternode/payments.cpp @@ -277,7 +277,7 @@ bool CMasternodePayments::GetBlockTxOuts(int nBlockHeight, CAmount blockReward, voutMasternodePaymentsRet.clear(); const CBlockIndex* pindex = WITH_LOCK(cs_main, return ::ChainActive()[nBlockHeight - 1]); - auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee(); + auto dmnPayee = deterministicMNManager->GetListForBlock(pindex).GetMNPayee(pindex); if (!dmnPayee) { return false; } diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 7703ab3abf4f..dc61319ab044 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -336,7 +336,8 @@ static UniValue masternode_winners(const JSONRPCRequest& request) int nStartHeight = std::max(nChainTipHeight - nCount, 1); for (int h = nStartHeight; h <= nChainTipHeight; h++) { - auto payee = deterministicMNManager->GetListForBlock(pindexTip->GetAncestor(h - 1)).GetMNPayee(); + const CBlockIndex* pIndex = pindexTip->GetAncestor(h - 1); + auto payee = deterministicMNManager->GetListForBlock(pIndex).GetMNPayee(pIndex); std::string strPayments = GetRequiredPaymentsString(h, payee); if (strFilter != "" && strPayments.find(strFilter) == std::string::npos) continue; obj.pushKV(strprintf("%d", h), strPayments); @@ -461,8 +462,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request) payeesArr.push_back(obj); } - // NOTE: we use _previous_ block to find a payee for the current one - const auto dmnPayee = deterministicMNManager->GetListForBlock(pindex->pprev).GetMNPayee(); + const auto dmnPayee = deterministicMNManager->GetListForBlock(pindex->pprev).GetMNPayee(pindex->pprev); protxObj.pushKV("proTxHash", dmnPayee == nullptr ? "" : dmnPayee->proTxHash.ToString()); protxObj.pushKV("amount", payedPerMasternode); protxObj.pushKV("payees", payeesArr); diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index f48fb65787e5..b46ff5f7c636 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -322,7 +322,7 @@ void FuncDIP3Protx(TestChainSetup& setup) // check MN reward payments for (size_t i = 0; i < 20; i++) { - auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); + auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(::ChainActive().Tip()); CBlock block = setup.CreateAndProcessBlock({}, setup.coinbaseKey); deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); @@ -380,7 +380,7 @@ void FuncDIP3Protx(TestChainSetup& setup) // test that the revoked MN does not get paid anymore for (size_t i = 0; i < 20; i++) { - auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); + auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(::ChainActive().Tip()); BOOST_ASSERT(dmnExpectedPayee->proTxHash != dmnHashes[0]); CBlock block = setup.CreateAndProcessBlock({}, setup.coinbaseKey); @@ -428,7 +428,7 @@ void FuncDIP3Protx(TestChainSetup& setup) // test that the revived MN gets payments again bool foundRevived = false; for (size_t i = 0; i < 20; i++) { - auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(); + auto dmnExpectedPayee = deterministicMNManager->GetListAtChainTip().GetMNPayee(::ChainActive().Tip()); if (dmnExpectedPayee->proTxHash == dmnHashes[0]) { foundRevived = true; } From 9feddeb6354866e8b39e3901b8f8a3ccb8853607 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 19:53:46 +0200 Subject: [PATCH 41/64] use irange --- src/evo/deterministicmns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 5b398f6fe8df..ef1b4d4da529 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -1231,7 +1231,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() curMNList.SetHeight(Params().GetConsensus().DIP0003Height - 1); curMNList.SetBlockHash(::ChainActive()[Params().GetConsensus().DIP0003Height - 1]->GetBlockHash()); - for (int nHeight = Params().GetConsensus().DIP0003Height; nHeight <= ::ChainActive().Height(); nHeight++) { + for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) { auto pindex = ::ChainActive()[nHeight]; CDeterministicMNList newMNList; From a1c7435e0928f2126ae62641cec897fd9802f359 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 19:56:26 +0200 Subject: [PATCH 42/64] removed useless check + relaxed constraints --- src/evo/deterministicmns.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index ef1b4d4da529..c60e21abdcd7 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -1305,8 +1305,6 @@ static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state) if (proTx.platformP2PPort != mainnetPlatformP2PPort) { return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-p2p-port"); } - } else if (proTx.platformP2PPort == mainnetPlatformP2PPort || proTx.platformP2PPort < 1 || proTx.platformP2PPort > std::numeric_limits::max()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-p2p-port"); } static int mainnetPlatformHTTPPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPlatformHTTPPort(); @@ -1314,8 +1312,6 @@ static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state) if (proTx.platformHTTPPort != mainnetPlatformHTTPPort) { return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-http-port"); } - } else if (proTx.platformHTTPPort == mainnetPlatformHTTPPort || proTx.platformHTTPPort < 1 || proTx.platformHTTPPort > std::numeric_limits::max()) { - return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-http-port"); } if (proTx.platformP2PPort == proTx.platformHTTPPort || From 7a30763cc89fa77493ad8fc46ca8a849a7c5a0f5 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 20:39:31 +0200 Subject: [PATCH 43/64] use enum for mn type --- src/evo/deterministicmns.cpp | 18 +++++++++--------- src/evo/deterministicmns.h | 14 ++++++++------ src/evo/simplifiedmns.cpp | 2 +- src/evo/simplifiedmns.h | 4 ++-- src/governance/object.cpp | 2 +- src/governance/vote.cpp | 2 +- src/rpc/evo.cpp | 4 ++-- src/rpc/masternode.cpp | 4 ++-- 8 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index c60e21abdcd7..039b716699f4 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -49,7 +49,7 @@ void CDeterministicMN::ToJson(UniValue& obj) const UniValue stateObj; pdmnState->ToJson(stateObj); - obj.pushKV("type", nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); + obj.pushKV("type", nType == MasternodeType::HighPerformance ? "HighPerformance" : "Regular"); obj.pushKV("proTxHash", proTxHash.ToString()); obj.pushKV("collateralHash", collateralOutpoint.hash.ToString()); obj.pushKV("collateralIndex", (int)collateralOutpoint.n); @@ -191,7 +191,7 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex) if (dmn->pdmnState->nLastPaidHeight == nHeight) { // We found the last MN Payee. // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if nConsecutivePayments < 3 - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->pdmnState->nConsecutivePayments < 3) { + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance && dmn->pdmnState->nConsecutivePayments < 3) { best = dmn; } } @@ -269,7 +269,7 @@ std::vector> CDeterministicMNList return; } if (onlyHighPerformanceMasternodes) { - if (dmn->nType != CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + if (dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) return; } // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN @@ -472,7 +472,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -513,7 +513,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate pubKeyOperator=%s", __func__, oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -573,7 +573,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { if (!DeleteUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -823,10 +823,10 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C if (!dmn) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash"); } - if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->nType != CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type"); } - if (proTx.nType == CProUpServTx::TYPE_REGULAR_MASTERNODE && dmn->nType != CDeterministicMN::TYPE_REGULAR_MASTERNODE) { + if (proTx.nType == CProUpServTx::TYPE_REGULAR_MASTERNODE && dmn->nType != CDeterministicMN::MasternodeType::Regular) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type"); } @@ -948,7 +948,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C // No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation // TODO: Skip this code once v20 is active // Note: If the payee wasn't found in the current block that's fine - if (payee->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (payee->nType == CDeterministicMN::MasternodeType::HighPerformance) { if (newState->nConsecutivePayments == 3) newState->nConsecutivePayments = 0; else diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 2fee288cdc2e..1c77fc8f9338 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -40,8 +40,10 @@ class CDeterministicMN uint64_t internalId{std::numeric_limits::max()}; public: - static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; - static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; + enum MasternodeType : std::uint16_t { + Regular = 0, + HighPerformance = 1 + }; static constexpr CAmount REGULAR_MASTERNODE_WEIGHT{1}; static constexpr CAmount REGULAR_MASTERNODE_COLLATERAL{1000 * COIN}; @@ -54,7 +56,7 @@ class CDeterministicMN CDeterministicMN() = delete; // no default constructor, must specify internalId explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : internalId(_internalId) { - highPerformanceMasternode ? nType = TYPE_HIGH_PERFORMANCE_MASTERNODE : nType = TYPE_REGULAR_MASTERNODE; + highPerformanceMasternode ? nType = MasternodeType::HighPerformance : nType = MasternodeType::Regular; // only non-initial values assert(_internalId != std::numeric_limits::max()); } @@ -68,7 +70,7 @@ class CDeterministicMN uint256 proTxHash; COutPoint collateralOutpoint; uint16_t nOperatorReward{0}; - uint16_t nType{TYPE_REGULAR_MASTERNODE}; + uint16_t nType{MasternodeType::Regular}; std::shared_ptr pdmnState; template @@ -97,7 +99,7 @@ class CDeterministicMN READWRITE(nType); } else { - nType = TYPE_REGULAR_MASTERNODE; + nType = MasternodeType::Regular; } } @@ -235,7 +237,7 @@ class CDeterministicMNList [[nodiscard]] size_t GetAllHPMNsCount() const { - return ranges::count_if(mnMap, [](const auto& p){ return p.second->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE;}); + return ranges::count_if(mnMap, [](const auto& p){ return p.second->nType == CDeterministicMN::MasternodeType::HighPerformance;}); } /** diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 3fd80e1fab10..f9df6ea51527 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -71,7 +71,7 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const obj.pushKV("isValid", isValid); obj.pushKV("nVersion", nVersion); obj.pushKV("nType", nType); - if (nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (nType == CDeterministicMN::MasternodeType::HighPerformance) { obj.pushKV("platformHTTPPort", platformHTTPPort); } diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 2f67cc96e404..706a3fa1299c 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -33,7 +33,7 @@ class CSimplifiedMNListEntry CBLSLazyPublicKey pubKeyOperator; CKeyID keyIDVoting; bool isValid{false}; - uint16_t nType{CDeterministicMN::TYPE_REGULAR_MASTERNODE}; + uint16_t nType{CDeterministicMN::MasternodeType::Regular}; uint16_t platformHTTPPort{0}; CScript scriptPayout; // mem-only CScript scriptOperatorPayout; // mem-only @@ -72,7 +72,7 @@ class CSimplifiedMNListEntry ); if (obj.nVersion == BASIC_BLS_VERSION) { READWRITE(obj.nType); - if (obj.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (obj.nType == CDeterministicMN::MasternodeType::HighPerformance) { READWRITE(obj.platformHTTPPort); } } diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 487c9f6d9656..fd3a792d5559 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -631,7 +631,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote // 4x times weight vote for HPMN owners. // No need to check if v19 is active since no HPMN are allowed to register before v19s auto dmn = mnList.GetMNByCollateral(votepair.first); - nCount += (dmn != nullptr && dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + nCount += (dmn != nullptr && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; } diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 2bb34167c772..4f226170c361 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -112,7 +112,7 @@ std::string CGovernanceVote::ToString() const { auto mnList = deterministicMNManager->GetListAtChainTip(); auto dmn = mnList.GetMNByCollateral(masternodeOutpoint); - int voteWeight = (dmn != nullptr && dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) + int voteWeight = (dmn != nullptr && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; std::ostringstream ostr; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 422c2f5b89a1..ef6fb97471a8 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -1008,7 +1008,7 @@ static UniValue protx_update_service(const JSONRPCRequest& request) if (!dmn) { throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); } - if (dmn->nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { throw std::runtime_error(strprintf("masternode with proTxHash %s is a HPMN", ptx.proTxHash.ToString())); } @@ -1131,7 +1131,7 @@ static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) if (!dmn) { throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); } - if (dmn->nType != CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { throw std::runtime_error(strprintf("masternode with proTxHash %s is not a HPMN", ptx.proTxHash.ToString())); } diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index dc61319ab044..f9fe4fd39f66 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -666,8 +666,8 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("address", dmn.pdmnState->addr.ToString()); objMN.pushKV("payee", payeeStr); objMN.pushKV("status", dmnToStatus(dmn)); - objMN.pushKV("type", dmn.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE ? "HighPerformance" : "Regular"); - if (dmn.nType == CDeterministicMN::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + objMN.pushKV("type", dmn.nType == CDeterministicMN::MasternodeType::HighPerformance ? "HighPerformance" : "Regular"); + if (dmn.nType == CDeterministicMN::MasternodeType::HighPerformance) { objMN.pushKV("platformNodeID", dmn.pdmnState->platformNodeID.ToString()); objMN.pushKV("platformP2PPort", dmn.pdmnState->platformP2PPort); objMN.pushKV("platformHTTPPort", dmn.pdmnState->platformHTTPPort); From 8b23b0945d8c610fb4d3761b945cfe6b7b71670c Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 20:49:02 +0200 Subject: [PATCH 44/64] reverted lines --- src/evo/dmnstate.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index 1cc4f0c3a3ac..b75645ebf465 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -201,6 +201,7 @@ class CDeterministicMNState h.Write(_confirmedHash.begin(), _confirmedHash.size()); h.Finalize(confirmedHashWithProRegTxHash.begin()); } + public: std::string ToString() const; From b513c9b311e5cf39467bce6509fa06f64ee95c59 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 20:52:26 +0200 Subject: [PATCH 45/64] refactor --- src/governance/object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/governance/object.cpp b/src/governance/object.cpp index fd3a792d5559..e78b1ed8624e 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -631,7 +631,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote // 4x times weight vote for HPMN owners. // No need to check if v19 is active since no HPMN are allowed to register before v19s auto dmn = mnList.GetMNByCollateral(votepair.first); - nCount += (dmn != nullptr && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) + if (dmn) nCount += (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; } From e232275e0236d9cd2bc6f7ec31b552ec1d448f27 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 21:02:05 +0200 Subject: [PATCH 46/64] IsLLMQTypeHPMNOnly --- src/llmq/utils.cpp | 4 ++-- src/llmq/utils.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/llmq/utils.cpp b/src/llmq/utils.cpp index 794ddafdf0e4..01d5b48e09c9 100644 --- a/src/llmq/utils.cpp +++ b/src/llmq/utils.cpp @@ -131,7 +131,7 @@ 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, IsPlatformLLMQType(llmqType)); + return allMns.CalculateQuorum(GetLLMQParams(llmqType).size, modifier, IsLLMQTypeHPMNOnly(llmqType)); } std::vector> ComputeQuorumMembersByQuarterRotation(Consensus::LLMQType llmqType, const CBlockIndex* pCycleQuorumBaseBlockIndex) @@ -673,7 +673,7 @@ bool IsInstantSendLLMQTypeShared() return false; } -bool IsPlatformLLMQType(Consensus::LLMQType llmqType) +bool IsLLMQTypeHPMNOnly(Consensus::LLMQType llmqType) { return Params().GetConsensus().llmqTypePlatform == llmqType; } diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 6088dd6bbbd8..9aa1eacba108 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -86,7 +86,7 @@ bool IsDIP0024Active(const CBlockIndex* pindex); bool IsV19Active(const CBlockIndex* pindex); const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex); static bool IsInstantSendLLMQTypeShared(); -static bool IsPlatformLLMQType(Consensus::LLMQType llmqType); +static bool IsLLMQTypeHPMNOnly(Consensus::LLMQType llmqType); /// Returns the state of `-llmq-data-recovery` bool QuorumDataRecoveryEnabled(); From 4352c9e23890242036703e78c14951d9503d8d56 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 7 Feb 2023 21:04:17 +0200 Subject: [PATCH 47/64] refactor --- src/evo/dmnstate.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index b75645ebf465..f088e7dcb8df 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -120,7 +120,6 @@ class CDeterministicMNState nPoSeBanHeight(s.nPoSeBanHeight), nRegisteredHeight(s.nRegisteredHeight), nLastPaidHeight(s.nLastPaidHeight), - nConsecutivePayments(0), nPoSePenalty(s.nPoSePenalty), nPoSeRevivedHeight(s.nPoSeRevivedHeight), nRevocationReason(s.nRevocationReason), @@ -131,10 +130,7 @@ class CDeterministicMNState keyIDVoting(s.keyIDVoting), addr(s.addr), scriptPayout(s.scriptPayout), - scriptOperatorPayout(s.scriptOperatorPayout), - platformNodeID(), - platformP2PPort(0), - platformHTTPPort(0){} + scriptOperatorPayout(s.scriptOperatorPayout){} template CDeterministicMNState(deserialize_type, Stream& s) { From aaf72b023961e7d4390924b808496e440a12130e Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 8 Feb 2023 11:00:31 +0200 Subject: [PATCH 48/64] brackets --- src/rpc/evo.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index ef6fb97471a8..05eeecafb75c 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -452,7 +452,6 @@ static void protx_register_submit_help(const JSONRPCRequest& request) }, }.Check(request); } - static UniValue protx_register_wrapper(const JSONRPCRequest& request, const bool specific_legacy_bls_scheme, const bool isExternalRegister, @@ -483,10 +482,12 @@ static UniValue protx_register_wrapper(const JSONRPCRequest& request, tx.nType = TRANSACTION_PROVIDER_REGISTER; CProRegTx ptx; - if (specific_legacy_bls_scheme) + if (specific_legacy_bls_scheme) { ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; - else + } + else { ptx.nVersion = CProRegTx::GetVersion(isV19active); + } ptx.nType = CProRegTx::TYPE_REGULAR_MASTERNODE; if (isFundRegister) { From ea129c2f94883d9cb609eb9efa5cf6a520db0990 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 8 Feb 2023 13:08:38 +0200 Subject: [PATCH 49/64] evo protx rpc refactor --- src/rpc/evo.cpp | 412 ++++++++++++------------------------------------ 1 file changed, 103 insertions(+), 309 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 05eeecafb75c..b8e2cace7a06 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -452,176 +452,6 @@ static void protx_register_submit_help(const JSONRPCRequest& request) }, }.Check(request); } -static UniValue protx_register_wrapper(const JSONRPCRequest& request, - const bool specific_legacy_bls_scheme, - const bool isExternalRegister, - const bool isFundRegister, - const bool isPrepareRegister) -{ - if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request); - } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request); - } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request); - } - - std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); - if (!wallet) return NullUniValue; - - if (isExternalRegister || isFundRegister) { - EnsureWalletIsUnlocked(wallet.get()); - } - - bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); - - size_t paramIdx = 0; - - CMutableTransaction tx; - tx.nVersion = 3; - tx.nType = TRANSACTION_PROVIDER_REGISTER; - - CProRegTx ptx; - if (specific_legacy_bls_scheme) { - ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; - } - else { - ptx.nVersion = CProRegTx::GetVersion(isV19active); - } - ptx.nType = CProRegTx::TYPE_REGULAR_MASTERNODE; - - if (isFundRegister) { - CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); - if (!IsValidDestination(collateralDest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); - } - CScript collateralScript = GetScriptForDestination(collateralDest); - - CTxOut collateralTxOut(CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, collateralScript); - tx.vout.emplace_back(collateralTxOut); - - paramIdx++; - } else { - uint256 collateralHash = ParseHashV(request.params[paramIdx], "collateralHash"); - int32_t collateralIndex = ParseInt32V(request.params[paramIdx + 1], "collateralIndex"); - if (collateralHash.IsNull() || collateralIndex < 0) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid hash or index: %s-%d", collateralHash.ToString(), collateralIndex)); - } - - ptx.collateralOutpoint = COutPoint(collateralHash, (uint32_t)collateralIndex); - paramIdx += 2; - - // TODO unlock on failure - LOCK(wallet->cs_wallet); - wallet->LockCoin(ptx.collateralOutpoint); - } - - if (request.params[paramIdx].get_str() != "") { - if (!Lookup(request.params[paramIdx].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { - throw std::runtime_error(strprintf("invalid network address %s", request.params[paramIdx].get_str())); - } - } - - ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address"); - CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", specific_legacy_bls_scheme); - CKeyID keyIDVoting = ptx.keyIDOwner; - - if (request.params[paramIdx + 3].get_str() != "") { - keyIDVoting = ParsePubKeyIDFromAddress(request.params[paramIdx + 3].get_str(), "voting address"); - } - - int64_t operatorReward; - if (!ParseFixedPoint(request.params[paramIdx + 4].getValStr(), 2, &operatorReward)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be a number"); - } - if (operatorReward < 0 || operatorReward > 10000) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "operatorReward must be between 0.00 and 100.00"); - } - ptx.nOperatorReward = operatorReward; - - CTxDestination payoutDest = DecodeDestination(request.params[paramIdx + 5].get_str()); - if (!IsValidDestination(payoutDest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); - } - - ptx.pubKeyOperator = pubKeyOperator; - ptx.keyIDVoting = keyIDVoting; - ptx.scriptPayout = GetScriptForDestination(payoutDest); - - if (!isFundRegister) { - // make sure fee calculation works - ptx.vchSig.resize(65); - } - - CTxDestination fundDest = payoutDest; - if (!request.params[paramIdx + 6].isNull()) { - fundDest = DecodeDestination(request.params[paramIdx + 6].get_str()); - if (!IsValidDestination(fundDest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 6].get_str()); - } - - FundSpecialTx(wallet.get(), tx, ptx, fundDest); - UpdateSpecialTxInputsHash(tx, ptx); - - bool fSubmit{true}; - if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 7].isNull()) { - fSubmit = ParseBoolV(request.params[paramIdx + 7], "submit"); - } - - if (isFundRegister) { - uint32_t collateralIndex = (uint32_t) -1; - for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { - collateralIndex = i; - break; - } - } - CHECK_NONFATAL(collateralIndex != (uint32_t) -1); - ptx.collateralOutpoint.n = collateralIndex; - - SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx, fSubmit); - } else { - // referencing external collateral - - Coin coin; - if (!GetUTXOCoin(ptx.collateralOutpoint, coin)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral not found: %s", ptx.collateralOutpoint.ToStringShort())); - } - CTxDestination txDest; - ExtractDestination(coin.out.scriptPubKey, txDest); - const PKHash *pkhash = std::get_if(&txDest); - if (!pkhash) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); - } - - if (isPrepareRegister) { - // external signing with collateral key - ptx.vchSig.clear(); - SetTxPayload(tx, ptx); - - UniValue ret(UniValue::VOBJ); - ret.pushKV("tx", EncodeHexTx(CTransaction(tx))); - ret.pushKV("collateralAddress", EncodeDestination(txDest)); - ret.pushKV("signMessage", ptx.MakeSignString()); - return ret; - } else { - // lets prove we own the collateral - LegacyScriptPubKeyMan* spk_man = wallet->GetLegacyScriptPubKeyMan(); - if (!spk_man) { - throw JSONRPCError(RPC_WALLET_ERROR, "This type of wallet does not support this command"); - } - - CKey key; - if (!spk_man->GetKey(CKeyID(*pkhash), key)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest))); - } - SignSpecialTxPayloadByString(tx, ptx, key); - SetTxPayload(tx, ptx); - return SignAndSendSpecialTx(request, tx, fSubmit); - } - } -} static void protx_register_fund_hpmn_help(const JSONRPCRequest& request) { @@ -725,17 +555,30 @@ static void protx_register_prepare_hpmn_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, - const bool isExternalRegister, - const bool isFundRegister, - const bool isPrepareRegister) +static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, + const bool specific_legacy_bls_scheme, + const bool isExternalRegister, + const bool isFundRegister, + const bool isPrepareRegister, + const bool isHPMNrequested) { - if (isFundRegister && (request.fHelp || (request.params.size() < 10 || request.params.size() > 12))) { - protx_register_fund_hpmn_help(request); - } else if (isExternalRegister && (request.fHelp || (request.params.size() < 11 || request.params.size() > 13))) { - protx_register_hpmn_help(request); - } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 11 && request.params.size() != 12))) { - protx_register_prepare_hpmn_help(request); + if (isHPMNrequested) { + if (isFundRegister && (request.fHelp || (request.params.size() < 10 || request.params.size() > 12))) { + protx_register_fund_hpmn_help(request); + } else if (isExternalRegister && (request.fHelp || (request.params.size() < 11 || request.params.size() > 13))) { + protx_register_hpmn_help(request); + } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 11 && request.params.size() != 12))) { + protx_register_prepare_hpmn_help(request); + } + } + else { + if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { + protx_register_fund_help(request); + } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { + protx_register_help(request); + } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { + protx_register_prepare_help(request); + } } std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); @@ -746,7 +589,7 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, } bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); - if (!isV19active) { + if (isHPMNrequested && !isV19active) { throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); } @@ -757,8 +600,13 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, tx.nType = TRANSACTION_PROVIDER_REGISTER; CProRegTx ptx; - ptx.nVersion = CProRegTx::GetVersion(isV19active); - ptx.nType = CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + if (specific_legacy_bls_scheme && !isHPMNrequested) { + ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; + } + else { + ptx.nVersion = CProRegTx::GetVersion(isV19active); + } + ptx.nType = isHPMNrequested ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); @@ -766,7 +614,9 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid collaterall address: %s", request.params[paramIdx].get_str())); } CScript collateralScript = GetScriptForDestination(collateralDest); - CTxOut collateralTxOut(CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL, collateralScript); + + CAmount fundCollateral = isHPMNrequested ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + CTxOut collateralTxOut(fundCollateral, collateralScript); tx.vout.emplace_back(collateralTxOut); paramIdx++; @@ -792,7 +642,7 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, } ptx.keyIDOwner = ParsePubKeyIDFromAddress(request.params[paramIdx + 1].get_str(), "owner address"); - CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address"); + CBLSPublicKey pubKeyOperator = ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", specific_legacy_bls_scheme && !isHPMNrequested); CKeyID keyIDVoting = ptx.keyIDOwner; if (request.params[paramIdx + 3].get_str() != "") { @@ -813,22 +663,26 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid payout address: %s", request.params[paramIdx + 5].get_str())); } - if (!IsHex(request.params[paramIdx + 6].get_str())) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); - } - ptx.platformNodeID.SetHex(request.params[paramIdx + 6].get_str()); + if (isHPMNrequested) { + if (!IsHex(request.params[paramIdx + 6].get_str())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); + } + ptx.platformNodeID.SetHex(request.params[paramIdx + 6].get_str()); - int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 7].get_str(), "platformP2PPort"); - if (!ValidatePort(requestedPlatformP2PPort)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); - } - ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); + int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 7].get_str(), "platformP2PPort"); + if (!ValidatePort(requestedPlatformP2PPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); + } + ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); + + int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[paramIdx + 8].get_str(), "platformHTTPPort"); + if (!ValidatePort(requestedPlatformHTTPPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); + } + ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); - int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[paramIdx + 8].get_str(), "platformHTTPPort"); - if (!ValidatePort(requestedPlatformHTTPPort)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); + paramIdx += 3; } - ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); ptx.pubKeyOperator = pubKeyOperator; ptx.keyIDVoting = keyIDVoting; @@ -840,24 +694,25 @@ static UniValue protx_register_hpmn_wrapper(const JSONRPCRequest& request, } CTxDestination fundDest = payoutDest; - if (!request.params[paramIdx + 9].isNull()) { - fundDest = DecodeDestination(request.params[paramIdx + 9].get_str()); + if (!request.params[paramIdx + 6].isNull()) { + fundDest = DecodeDestination(request.params[paramIdx + 6].get_str()); if (!IsValidDestination(fundDest)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 9].get_str()); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 6].get_str()); } FundSpecialTx(wallet.get(), tx, ptx, fundDest); UpdateSpecialTxInputsHash(tx, ptx); bool fSubmit{true}; - if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 10].isNull()) { - fSubmit = ParseBoolV(request.params[paramIdx + 10], "submit"); + if ((isExternalRegister || isFundRegister) && !request.params[paramIdx + 7].isNull()) { + fSubmit = ParseBoolV(request.params[paramIdx + 7], "submit"); } if (isFundRegister) { + CAmount fundCollateral = isHPMNrequested ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; uint32_t collateralIndex = (uint32_t) -1; for (uint32_t i = 0; i < tx.vout.size(); i++) { - if (tx.vout[i].nValue == CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL) { + if (tx.vout[i].nValue == fundCollateral) { collateralIndex = i; break; } @@ -914,7 +769,7 @@ static UniValue protx_register_hpmn(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister_hpmn"; bool isFundRegister = request.strMethod == "protxregister_fund_hpmn"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_hpmn"; - return protx_register_hpmn_wrapper(request, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_common_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister, true); } static UniValue protx_register(const JSONRPCRequest& request) @@ -922,7 +777,7 @@ static UniValue protx_register(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister"; bool isFundRegister = request.strMethod == "protxregister_fund"; bool isPrepareRegister = request.strMethod == "protxregister_prepare"; - return protx_register_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_common_wrapper(request, false, isExternalRegister, isFundRegister, isPrepareRegister, false); } static UniValue protx_register_legacy(const JSONRPCRequest& request) @@ -930,7 +785,7 @@ static UniValue protx_register_legacy(const JSONRPCRequest& request) bool isExternalRegister = request.strMethod == "protxregister_legacy"; bool isFundRegister = request.strMethod == "protxregister_fund_legacy"; bool isPrepareRegister = request.strMethod == "protxregister_prepare_legacy"; - return protx_register_wrapper(request, true, isExternalRegister, isFundRegister, isPrepareRegister); + return protx_register_common_wrapper(request, true, isExternalRegister, isFundRegister, isPrepareRegister, false); } static UniValue protx_register_submit(const JSONRPCRequest& request) @@ -986,81 +841,6 @@ static void protx_update_service_help(const JSONRPCRequest& request) }.Check(request); } -static UniValue protx_update_service(const JSONRPCRequest& request) -{ - protx_update_service_help(request); - - std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); - if (!wallet) return NullUniValue; - - EnsureWalletIsUnlocked(wallet.get()); - - CProUpServTx ptx; - ptx.nVersion = CProUpServTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); - ptx.proTxHash = ParseHashV(request.params[0], "proTxHash"); - - if (!Lookup(request.params[1].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { - throw std::runtime_error(strprintf("invalid network address %s", request.params[1].get_str())); - } - - CBLSSecretKey keyOperator = ParseBLSSecretKey(request.params[2].get_str(), "operatorKey"); - - auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash); - if (!dmn) { - throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); - } - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { - throw std::runtime_error(strprintf("masternode with proTxHash %s is a HPMN", ptx.proTxHash.ToString())); - } - - if (keyOperator.GetPublicKey() != dmn->pdmnState->pubKeyOperator.Get()) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("the operator key does not belong to the registered public key")); - } - - CMutableTransaction tx; - tx.nVersion = 3; - tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; - - // param operatorPayoutAddress - if (!request.params[3].isNull()) { - if (request.params[3].get_str().empty()) { - ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; - } else { - CTxDestination payoutDest = DecodeDestination(request.params[3].get_str()); - if (!IsValidDestination(payoutDest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[3].get_str())); - } - ptx.scriptOperatorPayout = GetScriptForDestination(payoutDest); - } - } else { - ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; - } - - CTxDestination feeSource; - - // param feeSourceAddress - if (!request.params[4].isNull()) { - feeSource = DecodeDestination(request.params[4].get_str()); - if (!IsValidDestination(feeSource)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[4].get_str()); - } else { - if (ptx.scriptOperatorPayout != CScript()) { - // use operator reward address as default source for fees - ExtractDestination(ptx.scriptOperatorPayout, feeSource); - } else { - // use payout address as default source for fees - ExtractDestination(dmn->pdmnState->scriptPayout, feeSource); - } - } - - FundSpecialTx(wallet.get(), tx, ptx, feeSource); - - SignSpecialTxPayloadByHash(tx, ptx, keyOperator); - SetTxPayload(tx, ptx); - - return SignAndSendSpecialTx(request, tx); -} - static void protx_update_service_hpmn_help(const JSONRPCRequest& request) { RPCHelpMan{"protx update_service_hpmn", @@ -1086,9 +866,15 @@ static void protx_update_service_hpmn_help(const JSONRPCRequest& request) }, }.Check(request); } -static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) + +static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, const bool isHPMNrequested) { - protx_update_service_hpmn_help(request); + if (isHPMNrequested) { + protx_update_service_hpmn_help(request); + } + else { + protx_update_service_help(request); + } std::shared_ptr const wallet = GetWalletForJSONRPCRequest(request); if (!wallet) return NullUniValue; @@ -1096,13 +882,13 @@ static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) EnsureWalletIsUnlocked(wallet.get()); bool isV19active = llmq::utils::IsV19Active(WITH_LOCK(cs_main, return ::ChainActive().Tip();)); - if (!isV19active) { + if (isHPMNrequested && !isV19active) { throw JSONRPCError(RPC_INVALID_REQUEST, "HPMN aren't allowed yet"); } CProUpServTx ptx; ptx.nVersion = CProUpServTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); - ptx.nType = CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE; + ptx.nType = isHPMNrequested ? CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProUpServTx::TYPE_REGULAR_MASTERNODE; ptx.proTxHash = ParseHashV(request.params[0], "proTxHash"); if (!Lookup(request.params[1].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { @@ -1111,30 +897,38 @@ static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) CBLSSecretKey keyOperator = ParseBLSSecretKey(request.params[2].get_str(), "operatorKey"); - if (!IsHex(request.params[3].get_str())) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); - } - ptx.platformNodeID.SetHex(request.params[3].get_str()); + size_t paramIdx = 3; + if (isHPMNrequested) { + if (!IsHex(request.params[paramIdx].get_str())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformNodeID must be hexadecimal string"); + } + ptx.platformNodeID.SetHex(request.params[paramIdx].get_str()); - int32_t requestedPlatformP2PPort = ParseInt32V(request.params[4].get_str(), "platformP2PPort"); - if (!ValidatePort(requestedPlatformP2PPort)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); - } - ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); + int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 1].get_str(), "platformP2PPort"); + if (!ValidatePort(requestedPlatformP2PPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); + } + ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); - int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[5].get_str(), "platformHTTPPort"); - if (!ValidatePort(requestedPlatformHTTPPort)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); + int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[paramIdx + 2].get_str(), "platformHTTPPort"); + if (!ValidatePort(requestedPlatformHTTPPort)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); + } + ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); + + paramIdx += 3; } - ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(ptx.proTxHash); if (!dmn) { throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); } - if (dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { + if (isHPMNrequested && dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { throw std::runtime_error(strprintf("masternode with proTxHash %s is not a HPMN", ptx.proTxHash.ToString())); } + else if (!isHPMNrequested && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + throw std::runtime_error(strprintf("masternode with proTxHash %s is a HPMN", ptx.proTxHash.ToString())); + } if (keyOperator.GetPublicKey() != dmn->pdmnState->pubKeyOperator.Get()) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("the operator key does not belong to the registered public key")); @@ -1145,13 +939,13 @@ static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) tx.nType = TRANSACTION_PROVIDER_UPDATE_SERVICE; // param operatorPayoutAddress - if (!request.params[6].isNull()) { - if (request.params[6].get_str().empty()) { + if (!request.params[paramIdx].isNull()) { + if (request.params[paramIdx].get_str().empty()) { ptx.scriptOperatorPayout = dmn->pdmnState->scriptOperatorPayout; } else { - CTxDestination payoutDest = DecodeDestination(request.params[6].get_str()); + CTxDestination payoutDest = DecodeDestination(request.params[paramIdx].get_str()); if (!IsValidDestination(payoutDest)) { - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[6].get_str())); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("invalid operator payout address: %s", request.params[paramIdx].get_str())); } ptx.scriptOperatorPayout = GetScriptForDestination(payoutDest); } @@ -1162,10 +956,10 @@ static UniValue protx_update_service_hpmn(const JSONRPCRequest& request) CTxDestination feeSource; // param feeSourceAddress - if (!request.params[7].isNull()) { - feeSource = DecodeDestination(request.params[7].get_str()); + if (!request.params[paramIdx + 1].isNull()) { + feeSource = DecodeDestination(request.params[paramIdx + 1].get_str()); if (!IsValidDestination(feeSource)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[7].get_str()); + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Dash address: ") + request.params[paramIdx + 1].get_str()); } else { if (ptx.scriptOperatorPayout != CScript()) { // use operator reward address as default source for fees @@ -1717,9 +1511,9 @@ static UniValue protx(const JSONRPCRequest& request) } else if (command == "protxregister_submit") { return protx_register_submit(new_request); } else if (command == "protxupdate_service") { - return protx_update_service(new_request); + return protx_update_service_common_wrapper(new_request, false); } else if (command == "protxupdate_service_hpmn") { - return protx_update_service_hpmn(new_request); + return protx_update_service_common_wrapper(new_request, true); } else if (command == "protxupdate_registrar") { return protx_update_registrar(new_request); } else if (command == "protxupdate_registrar_legacy") { From 851a5fb2e010db2aaa41b28311fd489471ffdad8 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Wed, 8 Feb 2023 13:24:07 +0200 Subject: [PATCH 50/64] clang format --- src/evo/deterministicmns.cpp | 12 +- src/evo/deterministicmns.h | 15 +- src/evo/dmnstate.h | 182 ++++++++-------- src/evo/providertx.cpp | 4 +- src/evo/providertx.h | 22 +- src/evo/simplifiedmns.cpp | 4 +- src/governance/object.cpp | 4 +- src/governance/vote.cpp | 4 +- src/llmq/params.h | 4 +- src/rpc/evo.cpp | 399 +++++++++++++++++------------------ 10 files changed, 311 insertions(+), 339 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 039b716699f4..f82545322a8c 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -762,8 +762,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C Coin coin; CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL - : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL + : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral)) { // should actually never get to this point as CheckProRegTx should have handled this case. // We do this additional check nevertheless to be 100% sure @@ -1098,8 +1098,8 @@ bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, u } CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL - : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL + : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; if (tx->vout[n].nValue != expectedCollateral) { return false; @@ -1385,8 +1385,8 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid COutPoint collateralOutpoint; CAmount expectedCollateral = ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL - : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL + : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; if (!ptx.collateralOutpoint.hash.IsNull()) { Coin coin; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 1c77fc8f9338..76ad75d25020 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -54,7 +54,8 @@ class CDeterministicMN static constexpr uint16_t MN_TYPE_FORMAT = 1; CDeterministicMN() = delete; // no default constructor, must specify internalId - explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : internalId(_internalId) + explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : + internalId(_internalId) { highPerformanceMasternode ? nType = MasternodeType::HighPerformance : nType = MasternodeType::Regular; // only non-initial values @@ -86,8 +87,7 @@ class CDeterministicMN CDeterministicMNState_Oldformat old_state; READWRITE(old_state); pdmnState = std::make_shared(old_state); - } - else { + } else { READWRITE(pdmnState); } // We need to read/write nType if: @@ -97,8 +97,7 @@ class CDeterministicMN // Otherwise, we can safely check with protocol versioning logic so we won't break old clients if (format_version >= MN_TYPE_FORMAT && (s.GetVersion() == CLIENT_VERSION || s.GetVersion() >= DMN_TYPE_PROTO_VERSION)) { READWRITE(nType); - } - else { + } else { nType = MasternodeType::Regular; } } @@ -109,7 +108,7 @@ class CDeterministicMN const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_TYPE_FORMAT); } - template + template void Unserialize(Stream& s, const uint8_t format_version = MN_TYPE_FORMAT) { SerializationOp(s, CSerActionUnserialize(), format_version); @@ -237,7 +236,7 @@ class CDeterministicMNList [[nodiscard]] size_t GetAllHPMNsCount() const { - return ranges::count_if(mnMap, [](const auto& p){ return p.second->nType == CDeterministicMN::MasternodeType::HighPerformance;}); + return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == CDeterministicMN::MasternodeType::HighPerformance; }); } /** @@ -480,7 +479,7 @@ class CDeterministicMNListDiff } } - template + template void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT) { updatedMNs.clear(); diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index f088e7dcb8df..e7ff5ab5b45a 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -25,7 +25,7 @@ namespace llmq class CFinalCommitment; } // namespace llmq -//TODO: To remove this in the future +// TODO: To remove this in the future class CDeterministicMNState_Oldformat { private: @@ -33,6 +33,7 @@ class CDeterministicMNState_Oldformat friend class CDeterministicMNStateDiff; friend class CDeterministicMNState; + public: int nRegisteredHeight{-1}; int nLastPaidHeight{0}; @@ -54,21 +55,20 @@ class CDeterministicMNState_Oldformat SERIALIZE_METHODS(CDeterministicMNState_Oldformat, obj) { READWRITE( - obj.nRegisteredHeight, - obj.nLastPaidHeight, - obj.nPoSePenalty, - obj.nPoSeRevivedHeight, - obj.nPoSeBanHeight, - obj.nRevocationReason, - obj.confirmedHash, - obj.confirmedHashWithProRegTxHash, - obj.keyIDOwner, - obj.pubKeyOperator, - obj.keyIDVoting, - obj.addr, - obj.scriptPayout, - obj.scriptOperatorPayout - ); + obj.nRegisteredHeight, + obj.nLastPaidHeight, + obj.nPoSePenalty, + obj.nPoSeRevivedHeight, + obj.nPoSeBanHeight, + obj.nRevocationReason, + obj.confirmedHash, + obj.confirmedHashWithProRegTxHash, + obj.keyIDOwner, + obj.pubKeyOperator, + obj.keyIDVoting, + obj.addr, + obj.scriptPayout, + obj.scriptOperatorPayout); } }; @@ -78,6 +78,7 @@ class CDeterministicMNState int nPoSeBanHeight{-1}; friend class CDeterministicMNStateDiff; + public: int nRegisteredHeight{-1}; int nLastPaidHeight{0}; @@ -106,31 +107,31 @@ class CDeterministicMNState public: CDeterministicMNState() = default; explicit CDeterministicMNState(const CProRegTx& proTx) : - keyIDOwner(proTx.keyIDOwner), - keyIDVoting(proTx.keyIDVoting), - addr(proTx.addr), - scriptPayout(proTx.scriptPayout), - platformNodeID(proTx.platformNodeID), - platformP2PPort(proTx.platformP2PPort), - platformHTTPPort(proTx.platformHTTPPort) + keyIDOwner(proTx.keyIDOwner), + keyIDVoting(proTx.keyIDVoting), + addr(proTx.addr), + scriptPayout(proTx.scriptPayout), + platformNodeID(proTx.platformNodeID), + platformP2PPort(proTx.platformP2PPort), + platformHTTPPort(proTx.platformHTTPPort) { pubKeyOperator.Set(proTx.pubKeyOperator); } explicit CDeterministicMNState(const CDeterministicMNState_Oldformat& s) : - nPoSeBanHeight(s.nPoSeBanHeight), - nRegisteredHeight(s.nRegisteredHeight), - nLastPaidHeight(s.nLastPaidHeight), - nPoSePenalty(s.nPoSePenalty), - nPoSeRevivedHeight(s.nPoSeRevivedHeight), - nRevocationReason(s.nRevocationReason), - confirmedHash(s.confirmedHash), - confirmedHashWithProRegTxHash(s.confirmedHashWithProRegTxHash), - keyIDOwner(s.keyIDOwner), - pubKeyOperator(s.pubKeyOperator), - keyIDVoting(s.keyIDVoting), - addr(s.addr), - scriptPayout(s.scriptPayout), - scriptOperatorPayout(s.scriptOperatorPayout){} + nPoSeBanHeight(s.nPoSeBanHeight), + nRegisteredHeight(s.nRegisteredHeight), + nLastPaidHeight(s.nLastPaidHeight), + nPoSePenalty(s.nPoSePenalty), + nPoSeRevivedHeight(s.nPoSeRevivedHeight), + nRevocationReason(s.nRevocationReason), + confirmedHash(s.confirmedHash), + confirmedHashWithProRegTxHash(s.confirmedHashWithProRegTxHash), + keyIDOwner(s.keyIDOwner), + pubKeyOperator(s.pubKeyOperator), + keyIDVoting(s.keyIDVoting), + addr(s.addr), + scriptPayout(s.scriptPayout), + scriptOperatorPayout(s.scriptOperatorPayout) {} template CDeterministicMNState(deserialize_type, Stream& s) { @@ -140,25 +141,24 @@ class CDeterministicMNState SERIALIZE_METHODS(CDeterministicMNState, obj) { READWRITE( - obj.nRegisteredHeight, - obj.nLastPaidHeight, - obj.nConsecutivePayments, - obj.nPoSePenalty, - obj.nPoSeRevivedHeight, - obj.nPoSeBanHeight, - obj.nRevocationReason, - obj.confirmedHash, - obj.confirmedHashWithProRegTxHash, - obj.keyIDOwner, - obj.pubKeyOperator, - obj.keyIDVoting, - obj.addr, - obj.scriptPayout, - obj.scriptOperatorPayout, - obj.platformNodeID, - obj.platformP2PPort, - obj.platformHTTPPort - ); + obj.nRegisteredHeight, + obj.nLastPaidHeight, + obj.nConsecutivePayments, + obj.nPoSePenalty, + obj.nPoSeRevivedHeight, + obj.nPoSeBanHeight, + obj.nRevocationReason, + obj.confirmedHash, + obj.confirmedHashWithProRegTxHash, + obj.keyIDOwner, + obj.pubKeyOperator, + obj.keyIDVoting, + obj.addr, + obj.scriptPayout, + obj.scriptOperatorPayout, + obj.platformNodeID, + obj.platformP2PPort, + obj.platformHTTPPort); } void ResetOperatorFields() @@ -197,7 +197,7 @@ class CDeterministicMNState h.Write(_confirmedHash.begin(), _confirmedHash.size()); h.Finalize(confirmedHashWithProRegTxHash.begin()); } - + public: std::string ToString() const; @@ -208,43 +208,43 @@ class CDeterministicMNStateDiff { public: enum Field : uint32_t { - Field_nRegisteredHeight = 0x0001, - Field_nLastPaidHeight = 0x0002, - Field_nPoSePenalty = 0x0004, - Field_nPoSeRevivedHeight = 0x0008, - Field_nPoSeBanHeight = 0x0010, - Field_nRevocationReason = 0x0020, - Field_confirmedHash = 0x0040, - Field_confirmedHashWithProRegTxHash = 0x0080, - Field_keyIDOwner = 0x0100, - Field_pubKeyOperator = 0x0200, - Field_keyIDVoting = 0x0400, - Field_addr = 0x0800, - Field_scriptPayout = 0x1000, - Field_scriptOperatorPayout = 0x2000, - Field_nConsecutivePayments = 0x4000, - Field_platformNodeID = 0x8000, - Field_platformP2PPort = 0x10000, - Field_platformHTTPPort = 0x20000, + Field_nRegisteredHeight = 0x0001, + Field_nLastPaidHeight = 0x0002, + Field_nPoSePenalty = 0x0004, + Field_nPoSeRevivedHeight = 0x0008, + Field_nPoSeBanHeight = 0x0010, + Field_nRevocationReason = 0x0020, + Field_confirmedHash = 0x0040, + Field_confirmedHashWithProRegTxHash = 0x0080, + Field_keyIDOwner = 0x0100, + Field_pubKeyOperator = 0x0200, + Field_keyIDVoting = 0x0400, + Field_addr = 0x0800, + Field_scriptPayout = 0x1000, + Field_scriptOperatorPayout = 0x2000, + Field_nConsecutivePayments = 0x4000, + Field_platformNodeID = 0x8000, + Field_platformP2PPort = 0x10000, + Field_platformHTTPPort = 0x20000, }; -#define DMN_STATE_DIFF_ALL_FIELDS \ - DMN_STATE_DIFF_LINE(nRegisteredHeight) \ - DMN_STATE_DIFF_LINE(nLastPaidHeight) \ - DMN_STATE_DIFF_LINE(nPoSePenalty) \ - DMN_STATE_DIFF_LINE(nPoSeRevivedHeight) \ - DMN_STATE_DIFF_LINE(nPoSeBanHeight) \ - DMN_STATE_DIFF_LINE(nRevocationReason) \ - DMN_STATE_DIFF_LINE(confirmedHash) \ +#define DMN_STATE_DIFF_ALL_FIELDS \ + DMN_STATE_DIFF_LINE(nRegisteredHeight) \ + DMN_STATE_DIFF_LINE(nLastPaidHeight) \ + DMN_STATE_DIFF_LINE(nPoSePenalty) \ + DMN_STATE_DIFF_LINE(nPoSeRevivedHeight) \ + DMN_STATE_DIFF_LINE(nPoSeBanHeight) \ + DMN_STATE_DIFF_LINE(nRevocationReason) \ + DMN_STATE_DIFF_LINE(confirmedHash) \ DMN_STATE_DIFF_LINE(confirmedHashWithProRegTxHash) \ - DMN_STATE_DIFF_LINE(keyIDOwner) \ - DMN_STATE_DIFF_LINE(pubKeyOperator) \ - DMN_STATE_DIFF_LINE(keyIDVoting) \ - DMN_STATE_DIFF_LINE(addr) \ - DMN_STATE_DIFF_LINE(scriptPayout) \ - DMN_STATE_DIFF_LINE(scriptOperatorPayout) \ - DMN_STATE_DIFF_LINE(platformNodeID) \ - DMN_STATE_DIFF_LINE(platformP2PPort) \ + DMN_STATE_DIFF_LINE(keyIDOwner) \ + DMN_STATE_DIFF_LINE(pubKeyOperator) \ + DMN_STATE_DIFF_LINE(keyIDVoting) \ + DMN_STATE_DIFF_LINE(addr) \ + DMN_STATE_DIFF_LINE(scriptPayout) \ + DMN_STATE_DIFF_LINE(scriptOperatorPayout) \ + DMN_STATE_DIFF_LINE(platformNodeID) \ + DMN_STATE_DIFF_LINE(platformP2PPort) \ DMN_STATE_DIFF_LINE(platformHTTPPort) public: diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 4ece3113aa70..c2c1300e3676 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -79,7 +79,7 @@ std::string CProRegTx::ToString() const } return strprintf("CProRegTx(nVersion=%d, nType=%d, collateralOutpoint=%s, addr=%s, nOperatorReward=%f, ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, scriptPayout=%s, platformNodeID=%s, platformP2PPort=%d, platformHTTPPort=%d)", - nVersion, nType, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); + nVersion, nType, collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); } maybe_error CProUpServTx::IsTriviallyValid(bool is_bls_legacy_scheme) const @@ -100,7 +100,7 @@ std::string CProUpServTx::ToString() const } return strprintf("CProUpServTx(nVersion=%d, nType=%d, proTxHash=%s, addr=%s, operatorPayoutAddress=%s, platformNodeID=%s, platformP2PPort=%d, platformHTTPPort=%d)", - nVersion, nType, proTxHash.ToString(), addr.ToString(), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); + nVersion, nType, proTxHash.ToString(), addr.ToString(), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); } maybe_error CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 3c364d956319..8c50c7de2a5d 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -53,9 +53,8 @@ class CProRegTx SERIALIZE_METHODS(CProRegTx, obj) { READWRITE( - obj.nVersion, - obj.nType - ); + obj.nVersion, + obj.nType); if (obj.nVersion == 0 || obj.nVersion > BASIC_BLS_VERSION) { // unknown version, bail out early return; @@ -73,10 +72,9 @@ class CProRegTx ); if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { READWRITE( - obj.platformNodeID, - obj.platformP2PPort, - obj.platformHTTPPort - ); + obj.platformNodeID, + obj.platformP2PPort, + obj.platformHTTPPort); } if (!(s.GetType() & SER_GETHASH)) { READWRITE(obj.vchSig); @@ -154,8 +152,7 @@ class CProUpServTx } if (obj.nVersion == BASIC_BLS_VERSION) { READWRITE( - obj.nType - ); + obj.nType); } READWRITE( obj.proTxHash, @@ -165,10 +162,9 @@ class CProUpServTx ); if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { READWRITE( - obj.platformNodeID, - obj.platformP2PPort, - obj.platformHTTPPort - ); + obj.platformNodeID, + obj.platformP2PPort, + obj.platformHTTPPort); } if (!(s.GetType() & SER_GETHASH)) { READWRITE( diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index f9df6ea51527..9a1d8293d92a 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -56,7 +56,7 @@ std::string CSimplifiedMNListEntry::ToString() const } return strprintf("CSimplifiedMNListEntry(nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d)", - nType, proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort); + nType, proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort); } void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const @@ -86,7 +86,7 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const } } -//TODO: Invistigate if we can delete this constructor +// TODO: Invistigate if we can delete this constructor CSimplifiedMNList::CSimplifiedMNList(const std::vector& smlEntries) { mnList.resize(smlEntries.size()); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index e78b1ed8624e..6397c4007f91 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -632,8 +632,8 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote // No need to check if v19 is active since no HPMN are allowed to register before v19s auto dmn = mnList.GetMNByCollateral(votepair.first); if (dmn) nCount += (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT - : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT + : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; } } return nCount; diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 4f226170c361..ea5ee3cda710 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -113,8 +113,8 @@ std::string CGovernanceVote::ToString() const auto mnList = deterministicMNManager->GetListAtChainTip(); auto dmn = mnList.GetMNByCollateral(masternodeOutpoint); int voteWeight = (dmn != nullptr && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT - : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; + ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT + : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" << nTime << ":" diff --git a/src/llmq/params.h b/src/llmq/params.h index 5afb316356cb..ff6bc7d1c548 100644 --- a/src/llmq/params.h +++ b/src/llmq/params.h @@ -30,9 +30,9 @@ enum class LLMQType : uint8_t { LLMQ_TEST_V17 = 102, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used // for testing only - LLMQ_TEST_DIP0024 = 103, // 4 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used + LLMQ_TEST_DIP0024 = 103, // 4 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestparams is used LLMQ_TEST_INSTANTSEND = 104, // 3 members, 2 (66%) threshold, one per hour. Params might differ when -llmqtestinstantsendparams is used - LLMQ_TEST_PLATFORM = 106, // 4 members, 2 (66%) threshold, one per hour. + LLMQ_TEST_PLATFORM = 106, // 4 members, 2 (66%) threshold, one per hour. // for devnets only. rotated version (v2) for devnets LLMQ_DEVNET_DIP0024 = 105 // 8 members, 4 (50%) threshold, one per hour. Params might differ when -llmqdevnetparams is used diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index b8e2cace7a06..a08f0bcae20c 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -44,112 +44,90 @@ static RPCArg GetRpcArg(const std::string& strParamName) { static const std::map mapParamHelp = { {"collateralAddress", - {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to send the collateral to."} - }, + {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to send the collateral to."}}, {"collateralHash", - {"collateralHash", RPCArg::Type::STR, RPCArg::Optional::NO, - "The collateral transaction hash."} - }, + {"collateralHash", RPCArg::Type::STR, RPCArg::Optional::NO, + "The collateral transaction hash."}}, {"collateralIndex", - {"collateralIndex", RPCArg::Type::NUM, RPCArg::Optional::NO, - "The collateral transaction output index."} - }, + {"collateralIndex", RPCArg::Type::NUM, RPCArg::Optional::NO, + "The collateral transaction output index."}}, {"feeSourceAddress", - {"feeSourceAddress", RPCArg::Type::STR, /* default */ "", - "If specified wallet will only use coins from this address to fund ProTx.\n" - "If not specified, payoutAddress is the one that is going to be used.\n" - "The private key belonging to this address must be known in your wallet."} - }, + {"feeSourceAddress", RPCArg::Type::STR, /* default */ "", + "If specified wallet will only use coins from this address to fund ProTx.\n" + "If not specified, payoutAddress is the one that is going to be used.\n" + "The private key belonging to this address must be known in your wallet."}}, {"fundAddress", - {"fundAddress", RPCArg::Type::STR, /* default */ "", - "If specified wallet will only use coins from this address to fund ProTx.\n" - "If not specified, payoutAddress is the one that is going to be used.\n" - "The private key belonging to this address must be known in your wallet."} - }, + {"fundAddress", RPCArg::Type::STR, /* default */ "", + "If specified wallet will only use coins from this address to fund ProTx.\n" + "If not specified, payoutAddress is the one that is going to be used.\n" + "The private key belonging to this address must be known in your wallet."}}, {"ipAndPort", - {"ipAndPort", RPCArg::Type::STR, RPCArg::Optional::NO, - "IP and port in the form \"IP:PORT\".\n" - "Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards."} - }, + {"ipAndPort", RPCArg::Type::STR, RPCArg::Optional::NO, + "IP and port in the form \"IP:PORT\".\n" + "Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards."}}, {"operatorKey", - {"operatorKey", RPCArg::Type::STR, RPCArg::Optional::NO, - "The operator BLS private key associated with the\n" - "registered operator public key."} - }, + {"operatorKey", RPCArg::Type::STR, RPCArg::Optional::NO, + "The operator BLS private key associated with the\n" + "registered operator public key."}}, {"operatorPayoutAddress", - {"operatorPayoutAddress", RPCArg::Type::STR, /* default */ "", - "The address used for operator reward payments.\n" - "Only allowed when the ProRegTx had a non-zero operatorReward value.\n" - "If set to an empty string, the currently active payout address is reused."} - }, + {"operatorPayoutAddress", RPCArg::Type::STR, /* default */ "", + "The address used for operator reward payments.\n" + "Only allowed when the ProRegTx had a non-zero operatorReward value.\n" + "If set to an empty string, the currently active payout address is reused."}}, {"operatorPubKey_register", - {"operatorPubKey_register", RPCArg::Type::STR, RPCArg::Optional::NO, - "The operator BLS public key. The BLS private key does not have to be known.\n" - "It has to match the BLS private key which is later used when operating the masternode."} - }, + {"operatorPubKey_register", RPCArg::Type::STR, RPCArg::Optional::NO, + "The operator BLS public key. The BLS private key does not have to be known.\n" + "It has to match the BLS private key which is later used when operating the masternode."}}, {"operatorPubKey_update", - {"operatorPubKey_update", RPCArg::Type::STR, RPCArg::Optional::NO, - "The operator BLS public key. The BLS private key does not have to be known.\n" - "It has to match the BLS private key which is later used when operating the masternode.\n" - "If set to an empty string, the currently active operator BLS public key is reused."} - }, + {"operatorPubKey_update", RPCArg::Type::STR, RPCArg::Optional::NO, + "The operator BLS public key. The BLS private key does not have to be known.\n" + "It has to match the BLS private key which is later used when operating the masternode.\n" + "If set to an empty string, the currently active operator BLS public key is reused."}}, {"operatorReward", - {"operatorReward", RPCArg::Type::STR, RPCArg::Optional::NO, - "The fraction in %% to share with the operator. The value must be\n" - "between 0.00 and 100.00."} - }, + {"operatorReward", RPCArg::Type::STR, RPCArg::Optional::NO, + "The fraction in %% to share with the operator. The value must be\n" + "between 0.00 and 100.00."}}, {"ownerAddress", - {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to use for payee updates and proposal voting.\n" - "The corresponding private key does not have to be known by your wallet.\n" - "The address must be unused and must differ from the collateralAddress."} - }, + {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to use for payee updates and proposal voting.\n" + "The corresponding private key does not have to be known by your wallet.\n" + "The address must be unused and must differ from the collateralAddress."}}, {"payoutAddress_register", - {"payoutAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to use for masternode reward payments."} - }, + {"payoutAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to use for masternode reward payments."}}, {"payoutAddress_update", - {"payoutAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to use for masternode reward payments.\n" - "If set to an empty string, the currently active payout address is reused."} - }, + {"payoutAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to use for masternode reward payments.\n" + "If set to an empty string, the currently active payout address is reused."}}, {"proTxHash", - {"proTxHash", RPCArg::Type::STR, RPCArg::Optional::NO, - "The hash of the initial ProRegTx."} - }, + {"proTxHash", RPCArg::Type::STR, RPCArg::Optional::NO, + "The hash of the initial ProRegTx."}}, {"reason", - {"reason", RPCArg::Type::NUM, /* default */ "", - "The reason for masternode service revocation."} - }, + {"reason", RPCArg::Type::NUM, /* default */ "", + "The reason for masternode service revocation."}}, {"submit", - {"submit", RPCArg::Type::BOOL, /* default */ "true", - "If true, the resulting transaction is sent to the network."} - }, + {"submit", RPCArg::Type::BOOL, /* default */ "true", + "If true, the resulting transaction is sent to the network."}}, {"votingAddress_register", - {"votingAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, - "The voting key address. The private key does not have to be known by your wallet.\n" - "It has to match the private key which is later used when voting on proposals.\n" - "If set to an empty string, ownerAddress will be used."} - }, + {"votingAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, + "The voting key address. The private key does not have to be known by your wallet.\n" + "It has to match the private key which is later used when voting on proposals.\n" + "If set to an empty string, ownerAddress will be used."}}, {"votingAddress_update", - {"votingAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, - "The voting key address. The private key does not have to be known by your wallet.\n" - "It has to match the private key which is later used when voting on proposals.\n" - "If set to an empty string, the currently active voting key address is reused."} - }, + {"votingAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, + "The voting key address. The private key does not have to be known by your wallet.\n" + "It has to match the private key which is later used when voting on proposals.\n" + "If set to an empty string, the currently active voting key address is reused."}}, {"platformNodeID", {"platformNodeID", RPCArg::Type::STR, RPCArg::Optional::NO, - "Platform P2P node ID, derived from P2P public key."} - }, + "Platform P2P node ID, derived from P2P public key."}}, {"platformP2PPort", {"platformP2PPort", RPCArg::Type::NUM, RPCArg::Optional::NO, - "TCP port of Dash Platform peer-to-peer communication between nodes (network byte order)."} - }, + "TCP port of Dash Platform peer-to-peer communication between nodes (network byte order)."}}, {"platformHTTPPort", {"platformHTTPPort", RPCArg::Type::NUM, RPCArg::Optional::NO, - "TCP port of Platform HTTP/API interface (network byte order). "} - }, + "TCP port of Platform HTTP/API interface (network byte order). "}}, }; auto it = mapParamHelp.find(strParamName); @@ -455,104 +433,105 @@ static void protx_register_submit_help(const JSONRPCRequest& request) static void protx_register_fund_hpmn_help(const JSONRPCRequest& request) { - RPCHelpMan{"protx register_fund_hpmn", - "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 4000 Dash\n" - "to the address specified by collateralAddress and will then function as the collateral of your\n" - "HPMN.\n" - "A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n" - "is fully deployed.\n" - + HELP_REQUIRING_PASSPHRASE, - { - GetRpcArg("collateralAddress"), - GetRpcArg("ipAndPort"), - GetRpcArg("ownerAddress"), - GetRpcArg("operatorPubKey_register"), - GetRpcArg("votingAddress_register"), - GetRpcArg("operatorReward"), - GetRpcArg("payoutAddress_register"), - GetRpcArg("platformNodeID"), - GetRpcArg("platformP2PPort"), - GetRpcArg("platformHTTPPort"), - GetRpcArg("fundAddress"), - GetRpcArg("submit"), - }, - { - RPCResult{"if \"submit\" is not set or set to true", - RPCResult::Type::STR_HEX, "txid", "The transaction id"}, - RPCResult{"if \"submit\" is set to false", - RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, - }, - RPCExamples{ - HelpExampleCli("protx", "register_fund_hpmn \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" 1000 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") - }, - }.Check(request); + RPCHelpMan{ + "protx register_fund_hpmn", + "\nCreates, funds and sends a ProTx to the network. The resulting transaction will move 4000 Dash\n" + "to the address specified by collateralAddress and will then function as the collateral of your\n" + "HPMN.\n" + "A few of the limitations you see in the arguments are temporary and might be lifted after DIP3\n" + "is fully deployed.\n" + + HELP_REQUIRING_PASSPHRASE, + { + GetRpcArg("collateralAddress"), + GetRpcArg("ipAndPort"), + GetRpcArg("ownerAddress"), + GetRpcArg("operatorPubKey_register"), + GetRpcArg("votingAddress_register"), + GetRpcArg("operatorReward"), + GetRpcArg("payoutAddress_register"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("fundAddress"), + GetRpcArg("submit"), + }, + { + RPCResult{"if \"submit\" is not set or set to true", + RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + RPCResult{"if \"submit\" is set to false", + RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, + }, + RPCExamples{ + HelpExampleCli("protx", "register_fund_hpmn \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" 1000 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, + } + .Check(request); } static void protx_register_hpmn_help(const JSONRPCRequest& request) { - RPCHelpMan{"protx register_hpmn", - "\nSame as \"protx register_fund_hpmn\", but with an externally referenced collateral.\n" - "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" - "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" - + HELP_REQUIRING_PASSPHRASE, - { - GetRpcArg("collateralHash"), - GetRpcArg("collateralIndex"), - GetRpcArg("ipAndPort"), - GetRpcArg("ownerAddress"), - GetRpcArg("operatorPubKey_register"), - GetRpcArg("votingAddress_register"), - GetRpcArg("operatorReward"), - GetRpcArg("payoutAddress_register"), - GetRpcArg("platformNodeID"), - GetRpcArg("platformP2PPort"), - GetRpcArg("platformHTTPPort"), - GetRpcArg("feeSourceAddress"), - GetRpcArg("submit"), - }, - { - RPCResult{"if \"submit\" is not set or set to true", - RPCResult::Type::STR_HEX, "txid", "The transaction id"}, - RPCResult{"if \"submit\" is set to false", - RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, - }, - RPCExamples{ - HelpExampleCli("protx", "register_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") - }, - }.Check(request); + RPCHelpMan{ + "protx register_hpmn", + "\nSame as \"protx register_fund_hpmn\", but with an externally referenced collateral.\n" + "The collateral is specified through \"collateralHash\" and \"collateralIndex\" and must be an unspent\n" + "transaction output spendable by this wallet. It must also not be used by any other masternode.\n" + + HELP_REQUIRING_PASSPHRASE, + { + GetRpcArg("collateralHash"), + GetRpcArg("collateralIndex"), + GetRpcArg("ipAndPort"), + GetRpcArg("ownerAddress"), + GetRpcArg("operatorPubKey_register"), + GetRpcArg("votingAddress_register"), + GetRpcArg("operatorReward"), + GetRpcArg("payoutAddress_register"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("feeSourceAddress"), + GetRpcArg("submit"), + }, + { + RPCResult{"if \"submit\" is not set or set to true", + RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + RPCResult{"if \"submit\" is set to false", + RPCResult::Type::STR_HEX, "hex", "The serialized signed ProTx in hex format"}, + }, + RPCExamples{ + HelpExampleCli("protx", "register_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, + } + .Check(request); } static void protx_register_prepare_hpmn_help(const JSONRPCRequest& request) { - RPCHelpMan{"protx register_prepare_hpmn", - "\nCreates an unsigned ProTx and a message that must be signed externally\n" - "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" - "The prepared transaction will also contain inputs and outputs to cover fees.\n", - { - GetRpcArg("collateralHash"), - GetRpcArg("collateralIndex"), - GetRpcArg("ipAndPort"), - GetRpcArg("ownerAddress"), - GetRpcArg("operatorPubKey_register"), - GetRpcArg("votingAddress_register"), - GetRpcArg("operatorReward"), - GetRpcArg("payoutAddress_register"), - GetRpcArg("platformNodeID"), - GetRpcArg("platformP2PPort"), - GetRpcArg("platformHTTPPort"), - GetRpcArg("feeSourceAddress"), - }, - RPCResult{ - RPCResult::Type::OBJ, "", "", - { - {RPCResult::Type::STR_HEX, "tx", "The serialized unsigned ProTx in hex format"}, - {RPCResult::Type::STR_HEX, "collateralAddress", "The collateral address"}, - {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, - }}, - RPCExamples{ - HelpExampleCli("protx", "register_prepare_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") - }, - }.Check(request); + RPCHelpMan{ + "protx register_prepare_hpmn", + "\nCreates an unsigned ProTx and a message that must be signed externally\n" + "with the private key that corresponds to collateralAddress to prove collateral ownership.\n" + "The prepared transaction will also contain inputs and outputs to cover fees.\n", + { + GetRpcArg("collateralHash"), + GetRpcArg("collateralIndex"), + GetRpcArg("ipAndPort"), + GetRpcArg("ownerAddress"), + GetRpcArg("operatorPubKey_register"), + GetRpcArg("votingAddress_register"), + GetRpcArg("operatorReward"), + GetRpcArg("payoutAddress_register"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("feeSourceAddress"), + }, + RPCResult{ + RPCResult::Type::OBJ, "", "", { + {RPCResult::Type::STR_HEX, "tx", "The serialized unsigned ProTx in hex format"}, + {RPCResult::Type::STR_HEX, "collateralAddress", "The collateral address"}, + {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, + }}, + RPCExamples{HelpExampleCli("protx", "register_prepare_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, + } + .Check(request); } static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, @@ -564,20 +543,19 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, { if (isHPMNrequested) { if (isFundRegister && (request.fHelp || (request.params.size() < 10 || request.params.size() > 12))) { - protx_register_fund_hpmn_help(request); + protx_register_fund_hpmn_help(request); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 11 || request.params.size() > 13))) { - protx_register_hpmn_help(request); + protx_register_hpmn_help(request); } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 11 && request.params.size() != 12))) { - protx_register_prepare_hpmn_help(request); + protx_register_prepare_hpmn_help(request); } - } - else { + } else { if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request); + protx_register_fund_help(request); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request); + protx_register_help(request); } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request); + protx_register_prepare_help(request); } } @@ -602,8 +580,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, CProRegTx ptx; if (specific_legacy_bls_scheme && !isHPMNrequested) { ptx.nVersion = CProRegTx::LEGACY_BLS_VERSION; - } - else { + } else { ptx.nVersion = CProRegTx::GetVersion(isV19active); } ptx.nType = isHPMNrequested ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; @@ -843,36 +820,35 @@ static void protx_update_service_help(const JSONRPCRequest& request) static void protx_update_service_hpmn_help(const JSONRPCRequest& request) { - RPCHelpMan{"protx update_service_hpmn", - "\nCreates and sends a ProUpServTx to the network. This will update the IP address and the Platform fields\n" - "of a HPMN.\n" - "If this is done for a HPMN that got PoSe-banned, the ProUpServTx will also revive this HPMN.\n" - + HELP_REQUIRING_PASSPHRASE, - { - GetRpcArg("proTxHash"), - GetRpcArg("ipAndPort"), - GetRpcArg("operatorKey"), - GetRpcArg("platformNodeID"), - GetRpcArg("platformP2PPort"), - GetRpcArg("platformHTTPPort"), - GetRpcArg("operatorPayoutAddress"), - GetRpcArg("feeSourceAddress"), - }, - RPCResult{ - RPCResult::Type::STR_HEX, "txid", "The transaction id" - }, - RPCExamples{ - HelpExampleCli("protx", "update_service_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" \"5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822") - }, - }.Check(request); + RPCHelpMan{ + "protx update_service_hpmn", + "\nCreates and sends a ProUpServTx to the network. This will update the IP address and the Platform fields\n" + "of a HPMN.\n" + "If this is done for a HPMN that got PoSe-banned, the ProUpServTx will also revive this HPMN.\n" + + HELP_REQUIRING_PASSPHRASE, + { + GetRpcArg("proTxHash"), + GetRpcArg("ipAndPort"), + GetRpcArg("operatorKey"), + GetRpcArg("platformNodeID"), + GetRpcArg("platformP2PPort"), + GetRpcArg("platformHTTPPort"), + GetRpcArg("operatorPayoutAddress"), + GetRpcArg("feeSourceAddress"), + }, + RPCResult{ + RPCResult::Type::STR_HEX, "txid", "The transaction id"}, + RPCExamples{ + HelpExampleCli("protx", "update_service_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" \"5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, + } + .Check(request); } static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, const bool isHPMNrequested) { if (isHPMNrequested) { protx_update_service_hpmn_help(request); - } - else { + } else { protx_update_service_help(request); } @@ -925,8 +901,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques } if (isHPMNrequested && dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { throw std::runtime_error(strprintf("masternode with proTxHash %s is not a HPMN", ptx.proTxHash.ToString())); - } - else if (!isHPMNrequested && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + } else if (!isHPMNrequested && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { throw std::runtime_error(strprintf("masternode with proTxHash %s is a HPMN", ptx.proTxHash.ToString())); } @@ -1463,7 +1438,8 @@ static UniValue protx_diff(const JSONRPCRequest& request) [[ noreturn ]] static void protx_help() { - RPCHelpMan{"protx", + RPCHelpMan{ + "protx", "Set of commands to execute ProTx related actions.\n" "To get help on individual commands, use \"help protx command\".\n" "\nAvailable commands:\n" @@ -1493,7 +1469,8 @@ static UniValue protx_diff(const JSONRPCRequest& request) }, RPCResults{}, RPCExamples{""}, - }.Throw(); + } + .Throw(); } static UniValue protx(const JSONRPCRequest& request) @@ -1524,7 +1501,7 @@ static UniValue protx(const JSONRPCRequest& request) return protx_revoke_legacy(new_request); } else #endif - if (command == "protxlist") { + if (command == "protxlist") { return protx_list(new_request); } else if (command == "protxinfo") { return protx_info(new_request); From 3b127175a571a18882895beaef4606da34834f86 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 9 Feb 2023 03:03:00 +0300 Subject: [PATCH 51/64] fix/tests: track nConsecutivePayments correctly, add tests --- src/evo/deterministicmns.cpp | 38 +++++++++++++++---- test/functional/feature_llmq_hpmn.py | 55 +++++++++++++++++----------- 2 files changed, 64 insertions(+), 29 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index f82545322a8c..cfb00857d702 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -190,8 +190,8 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex) ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { if (dmn->pdmnState->nLastPaidHeight == nHeight) { // We found the last MN Payee. - // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if nConsecutivePayments < 3 - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance && dmn->pdmnState->nConsecutivePayments < 3) { + // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if needed + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance && dmn->pdmnState->nConsecutivePayments < CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT) { best = dmn; } } @@ -942,21 +942,43 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C // The payee for the current block was determined by the previous block's list, but it might have disappeared in the // current block. We still pay that MN one last time, however. if (payee && newList.HasMN(payee->proTxHash)) { - auto newState = std::make_shared(*newList.GetMN(payee->proTxHash)->pdmnState); + auto dmn = newList.GetMN(payee->proTxHash); + auto newState = std::make_shared(*dmn->pdmnState); newState->nLastPaidHeight = nHeight; // Starting from v19 and until v20, HPMN will be paid 4 blocks in a row // No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation // TODO: Skip this code once v20 is active // Note: If the payee wasn't found in the current block that's fine - if (payee->nType == CDeterministicMN::MasternodeType::HighPerformance) { - if (newState->nConsecutivePayments == 3) - newState->nConsecutivePayments = 0; - else - newState->nConsecutivePayments++; + if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + ++newState->nConsecutivePayments; + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s is a HPMN, bumping nConsecutivePayments to %d\n", + __func__, dmn->proTxHash.ToString(), newState->nConsecutivePayments); + } } newList.UpdateMN(payee->proTxHash, newState); + dmn = newList.GetMN(payee->proTxHash); + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s, nConsecutivePayments=%d\n", + __func__, dmn->proTxHash.ToString(), dmn->pdmnState->nConsecutivePayments); + } } + // reset nConsecutivePayments on non-paid HPMNs + auto newList2 = newList; + newList2.ForEachMN(false, [&](auto& dmn) { + if (payee != nullptr && dmn.proTxHash == payee->proTxHash) return; + if (dmn.nType != CDeterministicMN::MasternodeType::HighPerformance) return; + if (dmn.pdmnState->nConsecutivePayments == 0) return; + if (debugLogs) { + LogPrintf("CDeterministicMNManager::%s -- MN %s, reset nConsecutivePayments %d->0\n", + __func__, dmn.proTxHash.ToString(), dmn.pdmnState->nConsecutivePayments); + } + auto newState = std::make_shared(*dmn.pdmnState); + newState->nConsecutivePayments = 0; + newList.UpdateMN(dmn.proTxHash, newState); + }); + mnListRet = std::move(newList); return true; diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index c2bb706107f3..0b94f6c115bb 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -87,30 +87,43 @@ def run_test(self): return def test_hpmmn_payements(self, window_analysis): - mn_payees = list() - + current_hpmn = None + consecutive_paymments = 0 for i in range(0, window_analysis): - payee_info = self.get_mn_payee_for_block(self.nodes[0].getbestblockhash()) - mn_payees.append(payee_info) + payee = self.get_mn_payee_for_block(self.nodes[0].getbestblockhash()) + if payee is not None and payee.hpmn: + if current_hpmn is not None and payee.proTxHash == current_hpmn.proTxHash: + # same HPMN + assert consecutive_paymments > 0 + consecutive_paymments += 1 + consecutive_paymments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments'] + assert_equal(consecutive_paymments, consecutive_paymments_rpc) + else: + # new HPMN + if current_hpmn is not None: + # make sure the old one was paid 4 times in a row + assert_equal(consecutive_paymments, 4) + consecutive_paymments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments'] + # old HPMN should have its nConsecutivePayments reset to 0 + assert_equal(consecutive_paymments_rpc, 0) + current_hpmn = payee + consecutive_paymments = 1 + consecutive_paymments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments'] + assert_equal(consecutive_paymments, consecutive_paymments_rpc) + else: + # not a HPMN + if current_hpmn is not None: + # make sure the old one was paid 4 times in a row + assert_equal(consecutive_paymments, 4) + consecutive_paymments_rpc = self.nodes[0].protx('info', current_hpmn.proTxHash)['state']['consecutivePayments'] + # old HPMN should have its nConsecutivePayments reset to 0 + assert_equal(consecutive_paymments_rpc, 0) + current_hpmn = None + consecutive_paymments = 0 self.nodes[0].generate(1) - self.sync_blocks() - - verified_hpmn = None - for i in range(len(mn_payees)): - # Start checking from the first payee different from the first element of the window analysis - if i > 0 and mn_payees[i] != mn_payees[0]: - # Check only HPMN - if mn_payees[i].hpmn: - # Skip already checked payee - if mn_payees[i].proTxHash == verified_hpmn: - continue - # Verify that current HPMN is paid for 4 blocks in a row - for j in range(1, 4): - # Avoid overflow check - if (i + j) < len(mn_payees): - assert_equal(mn_payees[i].proTxHash, mn_payees[i+j].proTxHash) - verified_hpmn = mn_payees[i].proTxHash + if i % 8 == 0: + self.sync_blocks() def get_mn_payee_for_block(self, block_hash): mn_payee_info = self.nodes[0].masternode("payments", block_hash)[0] From dfcf69ed8c27fd597424bdcd838cc0ff793bedd8 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 9 Feb 2023 03:03:25 +0300 Subject: [PATCH 52/64] fix: add missing `nConsecutivePayments` to `DMN_STATE_DIFF_ALL_FIELDS` --- src/evo/dmnstate.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index e7ff5ab5b45a..2e41cf16ea4d 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -243,6 +243,7 @@ class CDeterministicMNStateDiff DMN_STATE_DIFF_LINE(addr) \ DMN_STATE_DIFF_LINE(scriptPayout) \ DMN_STATE_DIFF_LINE(scriptOperatorPayout) \ + DMN_STATE_DIFF_LINE(nConsecutivePayments) \ DMN_STATE_DIFF_LINE(platformNodeID) \ DMN_STATE_DIFF_LINE(platformP2PPort) \ DMN_STATE_DIFF_LINE(platformHTTPPort) From 50816b84a4a6da7e6f60a58799b9111f86ee6e14 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 9 Feb 2023 03:04:58 +0300 Subject: [PATCH 53/64] fix: tweak `masternodelist` output to match dmnstate --- src/rpc/masternode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index f9fe4fd39f66..8d3d8209c81d 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -673,7 +673,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("platformHTTPPort", dmn.pdmnState->platformHTTPPort); } objMN.pushKV("pospenaltyscore", dmn.pdmnState->nPoSePenalty); - objMN.pushKV("nConsecutivePayments", dmn.pdmnState->nConsecutivePayments); + objMN.pushKV("consecutivePayments", dmn.pdmnState->nConsecutivePayments); objMN.pushKV("lastpaidtime", dmnToLastPaidTime(dmn)); objMN.pushKV("lastpaidblock", dmn.pdmnState->nLastPaidHeight); objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner))); From 26d2d10624111ce88b929c5467a18664b33c7396 Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Thu, 9 Feb 2023 03:07:07 +0300 Subject: [PATCH 54/64] fix/tests: fix `masternode payments` rpc, add tests it was picking the wrong DMN as a payee... --- src/rpc/masternode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 8d3d8209c81d..628fd038ebc9 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -462,6 +462,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request) payeesArr.push_back(obj); } + // NOTE: we use _previous_ block to find a payee for the current one const auto dmnPayee = deterministicMNManager->GetListForBlock(pindex->pprev).GetMNPayee(pindex->pprev); protxObj.pushKV("proTxHash", dmnPayee == nullptr ? "" : dmnPayee->proTxHash.ToString()); protxObj.pushKV("amount", payedPerMasternode); From a6337184ae03b0e1c71adc4a7d82e10996c8f85d Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sun, 12 Feb 2023 15:01:32 +0200 Subject: [PATCH 55/64] rebase adjustement --- src/rpc/evo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index a08f0bcae20c..919c4488c27d 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -708,8 +708,8 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, } CTxDestination txDest; ExtractDestination(coin.out.scriptPubKey, txDest); - const CKeyID *keyID = std::get_if(&txDest); - if (!keyID) { + const PKHash *pkhash = std::get_if(&txDest); + if (!pkhash) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral type not supported: %s", ptx.collateralOutpoint.ToStringShort())); } @@ -731,7 +731,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, } CKey key; - if (!spk_man->GetKey(*keyID, key)) { + if (!spk_man->GetKey(CKeyID(*pkhash), key)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("collateral key not in wallet: %s", EncodeDestination(txDest))); } SignSpecialTxPayloadByString(tx, ptx, key); From 0448ac4633ee8855252abf27ba7253071260dc8c Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Sun, 12 Feb 2023 15:27:31 +0200 Subject: [PATCH 56/64] Include PlatformNodeID in SML --- src/evo/simplifiedmns.cpp | 8 +++++--- src/evo/simplifiedmns.h | 5 ++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 9a1d8293d92a..f8ec58bf4f92 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -32,7 +32,8 @@ CSimplifiedMNListEntry::CSimplifiedMNListEntry(const CDeterministicMN& dmn) : scriptPayout(dmn.pdmnState->scriptPayout), scriptOperatorPayout(dmn.pdmnState->scriptOperatorPayout), nType(dmn.nType), - platformHTTPPort(dmn.pdmnState->platformHTTPPort) + platformHTTPPort(dmn.pdmnState->platformHTTPPort), + platformNodeID(dmn.pdmnState->platformNodeID) { } @@ -55,8 +56,8 @@ std::string CSimplifiedMNListEntry::ToString() const operatorPayoutAddress = EncodeDestination(dest); } - return strprintf("CSimplifiedMNListEntry(nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d)", - nType, proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort); + return strprintf("CSimplifiedMNListEntry(nType=%d, proRegTxHash=%s, confirmedHash=%s, service=%s, pubKeyOperator=%s, votingAddress=%s, isValid=%d, payoutAddress=%s, operatorPayoutAddress=%s, platformHTTPPort=%d, platformNodeID=%s)", + nType, proRegTxHash.ToString(), confirmedHash.ToString(), service.ToString(false), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), isValid, payoutAddress, operatorPayoutAddress, platformHTTPPort, platformNodeID.ToString()); } void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const @@ -73,6 +74,7 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const obj.pushKV("nType", nType); if (nType == CDeterministicMN::MasternodeType::HighPerformance) { obj.pushKV("platformHTTPPort", platformHTTPPort); + obj.pushKV("platformNodeID", platformNodeID.ToString()); } if (!extended) return; diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 706a3fa1299c..746da90b9475 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -35,6 +35,7 @@ class CSimplifiedMNListEntry bool isValid{false}; uint16_t nType{CDeterministicMN::MasternodeType::Regular}; uint16_t platformHTTPPort{0}; + uint160 platformNodeID{}; CScript scriptPayout; // mem-only CScript scriptOperatorPayout; // mem-only uint16_t nVersion{LEGACY_BLS_VERSION}; // mem-only @@ -52,7 +53,8 @@ class CSimplifiedMNListEntry isValid == rhs.isValid && nVersion == rhs.nVersion && nType == rhs.nType && - platformHTTPPort == rhs.platformHTTPPort; + platformHTTPPort == rhs.platformHTTPPort && + platformNodeID == rhs.platformNodeID; } bool operator!=(const CSimplifiedMNListEntry& rhs) const @@ -74,6 +76,7 @@ class CSimplifiedMNListEntry READWRITE(obj.nType); if (obj.nType == CDeterministicMN::MasternodeType::HighPerformance) { READWRITE(obj.platformHTTPPort); + READWRITE(obj.platformNodeID); } } } From 86440d4759ab7ce223710e5bba30efa334961ced Mon Sep 17 00:00:00 2001 From: pasta Date: Fri, 10 Feb 2023 20:21:09 -0600 Subject: [PATCH 57/64] refac: consolidate MasternodeType logic --- src/Makefile.am | 1 + src/evo/deterministicmns.cpp | 45 +++++++++----------- src/evo/deterministicmns.h | 25 +++++------ src/evo/providertx.cpp | 3 +- src/evo/providertx.h | 21 ++++----- src/evo/simplifiedmns.cpp | 2 +- src/evo/simplifiedmns.h | 5 ++- src/governance/object.cpp | 4 +- src/governance/vote.cpp | 5 +-- src/rpc/evo.cpp | 14 +++--- src/rpc/masternode.cpp | 4 +- src/test/block_reward_reallocation_tests.cpp | 2 +- src/test/evo_deterministicmns_tests.cpp | 18 ++++---- src/wallet/wallet.cpp | 4 +- 14 files changed, 76 insertions(+), 77 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 6e32c294e2e0..09073e0fd7d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -165,6 +165,7 @@ BITCOIN_CORE_H = \ cuckoocache.h \ ctpl_stl.h \ cxxtimer.hpp \ + evo/dmn_types.h \ evo/cbtx.h \ evo/deterministicmns.h \ evo/dmnstate.h \ diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index cfb00857d702..868f275060c4 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,7 @@ void CDeterministicMN::ToJson(UniValue& obj) const UniValue stateObj; pdmnState->ToJson(stateObj); - obj.pushKV("type", nType == MasternodeType::HighPerformance ? "HighPerformance" : "Regular"); + obj.pushKV("type", nType == MnType::HighPerformance.index ? "HighPerformance" : "Regular"); obj.pushKV("proTxHash", proTxHash.ToString()); obj.pushKV("collateralHash", collateralOutpoint.hash.ToString()); obj.pushKV("collateralIndex", (int)collateralOutpoint.n); @@ -191,7 +192,7 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex) if (dmn->pdmnState->nLastPaidHeight == nHeight) { // We found the last MN Payee. // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if needed - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance && dmn->pdmnState->nConsecutivePayments < CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT) { + if (dmn->nType == MnType::HighPerformance.index && dmn->pdmnState->nConsecutivePayments < MnType::HighPerformance.voting_weight) { best = dmn; } } @@ -269,7 +270,7 @@ std::vector> CDeterministicMNList return; } if (onlyHighPerformanceMasternodes) { - if (dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) + if (dmn->nType != MnType::HighPerformance.index) return; } // calculate sha256(sha256(proTxHash, confirmedHash), modifier) per MN @@ -472,7 +473,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + if (dmn->nType == MnType::HighPerformance.index) { if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -513,7 +514,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate pubKeyOperator=%s", __func__, oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + if (dmn->nType == MnType::HighPerformance.index) { if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't update a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -573,7 +574,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); } - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + if (dmn->nType == MnType::HighPerformance.index) { if (!DeleteUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a duplicate platformNodeID=%s", __func__, @@ -746,11 +747,11 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } - if (proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && !llmq::utils::IsV19Active(pindexPrev)) { + if (proTx.nType == MnType::HighPerformance.index && !llmq::utils::IsV19Active(pindexPrev)) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } - auto dmn = std::make_shared(newList.GetTotalRegisteredCount(), proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE); + auto dmn = std::make_shared(newList.GetTotalRegisteredCount(), proTx.nType == MnType::HighPerformance.index); dmn->proTxHash = tx.GetHash(); // collateralOutpoint is either pointing to an external collateral or to the ProRegTx itself @@ -761,9 +762,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } Coin coin; - CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL - : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount; if (!proTx.collateralOutpoint.hash.IsNull() && (!view.GetCoin(dmn->collateralOutpoint, coin) || coin.IsSpent() || coin.out.nValue != expectedCollateral)) { // should actually never get to this point as CheckProRegTx should have handled this case. // We do this additional check nevertheless to be 100% sure @@ -811,7 +810,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } - if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && !llmq::utils::IsV19Active(pindexPrev)) { + if (proTx.nType == MnType::HighPerformance.index && !llmq::utils::IsV19Active(pindexPrev)) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-payload"); } @@ -823,17 +822,17 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C if (!dmn) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-hash"); } - if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE && dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { + if (proTx.nType == MnType::HighPerformance.index && dmn->nType != MnType::HighPerformance.index) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type"); } - if (proTx.nType == CProUpServTx::TYPE_REGULAR_MASTERNODE && dmn->nType != CDeterministicMN::MasternodeType::Regular) { + if (proTx.nType == MnType::Regular.index && dmn->nType != MnType::Regular.index) { return _state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-protx-type"); } auto newState = std::make_shared(*dmn->pdmnState); newState->addr = proTx.addr; newState->scriptOperatorPayout = proTx.scriptOperatorPayout; - if (proTx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (proTx.nType == MnType::HighPerformance.index) { newState->platformNodeID = proTx.platformNodeID; newState->platformP2PPort = proTx.platformP2PPort; newState->platformHTTPPort = proTx.platformHTTPPort; @@ -949,7 +948,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C // No need to check if v19 is active, since HPMN ProRegTx are allowed only after v19 activation // TODO: Skip this code once v20 is active // Note: If the payee wasn't found in the current block that's fine - if (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + if (dmn->nType == MnType::HighPerformance.index) { ++newState->nConsecutivePayments; if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s is a HPMN, bumping nConsecutivePayments to %d\n", @@ -968,7 +967,7 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C auto newList2 = newList; newList2.ForEachMN(false, [&](auto& dmn) { if (payee != nullptr && dmn.proTxHash == payee->proTxHash) return; - if (dmn.nType != CDeterministicMN::MasternodeType::HighPerformance) return; + if (dmn.nType != MnType::HighPerformance.index) return; if (dmn.pdmnState->nConsecutivePayments == 0) return; if (debugLogs) { LogPrintf("CDeterministicMNManager::%s -- MN %s, reset nConsecutivePayments %d->0\n", @@ -1119,9 +1118,7 @@ bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, u return false; } - CAmount expectedCollateral = proTx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL - : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount; if (tx->vout[n].nValue != expectedCollateral) { return false; @@ -1396,7 +1393,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid return false; } - if (ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (ptx.nType == MnType::HighPerformance.index) { if (!CheckPlatformFields(ptx, state)) { return false; } @@ -1406,9 +1403,7 @@ bool CheckProRegTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValid const PKHash *keyForPayloadSig = nullptr; COutPoint collateralOutpoint; - CAmount expectedCollateral = ptx.nType == CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL - : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + CAmount expectedCollateral = GetMnType(ptx.nType).collat_amount; if (!ptx.collateralOutpoint.hash.IsNull()) { Coin coin; @@ -1509,7 +1504,7 @@ bool CheckProUpServTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVa return false; } - if (ptx.nType == CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (ptx.nType == MnType::HighPerformance.index) { if (!CheckPlatformFields(ptx, state)) { return false; } diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 76ad75d25020..d9adbff12752 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -40,15 +41,15 @@ class CDeterministicMN uint64_t internalId{std::numeric_limits::max()}; public: - enum MasternodeType : std::uint16_t { - Regular = 0, - HighPerformance = 1 - }; +// enum MasternodeType : std::uint16_t { +// Regular = 0, +// HighPerformance = 1 +// }; - static constexpr CAmount REGULAR_MASTERNODE_WEIGHT{1}; - static constexpr CAmount REGULAR_MASTERNODE_COLLATERAL{1000 * COIN}; - static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_WEIGHT{4}; - static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_COLLATERAL{REGULAR_MASTERNODE_COLLATERAL * CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT}; +// static constexpr CAmount REGULAR_MASTERNODE_WEIGHT{1}; +// static constexpr CAmount REGULAR_MASTERNODE_COLLATERAL{1000 * COIN}; +// static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_WEIGHT{4}; +// static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_COLLATERAL{REGULAR_MASTERNODE_COLLATERAL * CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT}; static constexpr uint16_t CURRENT_MN_FORMAT = 0; static constexpr uint16_t MN_TYPE_FORMAT = 1; @@ -57,7 +58,7 @@ class CDeterministicMN explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : internalId(_internalId) { - highPerformanceMasternode ? nType = MasternodeType::HighPerformance : nType = MasternodeType::Regular; + highPerformanceMasternode ? nType = MnType::HighPerformance.index : nType = MnType::Regular.index; // only non-initial values assert(_internalId != std::numeric_limits::max()); } @@ -71,7 +72,7 @@ class CDeterministicMN uint256 proTxHash; COutPoint collateralOutpoint; uint16_t nOperatorReward{0}; - uint16_t nType{MasternodeType::Regular}; + uint16_t nType{MnType::Regular.index}; std::shared_ptr pdmnState; template @@ -98,7 +99,7 @@ class CDeterministicMN if (format_version >= MN_TYPE_FORMAT && (s.GetVersion() == CLIENT_VERSION || s.GetVersion() >= DMN_TYPE_PROTO_VERSION)) { READWRITE(nType); } else { - nType = MasternodeType::Regular; + nType = MnType::Regular.index; } } @@ -236,7 +237,7 @@ class CDeterministicMNList [[nodiscard]] size_t GetAllHPMNsCount() const { - return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == CDeterministicMN::MasternodeType::HighPerformance; }); + return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == MnType::HighPerformance.index; }); } /** diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index c2c1300e3676..54c67321341a 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include @@ -14,7 +15,7 @@ maybe_error CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme) const if (nVersion == 0 || nVersion > GetVersion(is_bls_legacy_scheme)) { return {ValidationInvalidReason::CONSENSUS, "bad-protx-version"}; } - if (nType != TYPE_REGULAR_MASTERNODE && nType != TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (nType != MnType::Regular.index && nType != MnType::HighPerformance.index) { return {ValidationInvalidReason::CONSENSUS, "bad-protx-type"}; } if (nMode != 0) { diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 8c50c7de2a5d..26c505697fad 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -15,6 +15,7 @@ #include #include #include +#include class CBlockIndex; class CCoinsViewCache; @@ -26,8 +27,8 @@ class CProRegTx static constexpr auto SPECIALTX_TYPE = TRANSACTION_PROVIDER_REGISTER; static constexpr uint16_t LEGACY_BLS_VERSION = 1; static constexpr uint16_t BASIC_BLS_VERSION = 2; - static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; - static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; +// static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; +// static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; [[nodiscard]] static constexpr auto GetVersion(const bool is_basic_scheme_active) -> uint16_t { @@ -35,7 +36,7 @@ class CProRegTx } uint16_t nVersion{LEGACY_BLS_VERSION}; // message version - uint16_t nType{TYPE_REGULAR_MASTERNODE}; + uint16_t nType{MnType::Regular.index}; uint16_t nMode{0}; // only 0 supported for now COutPoint collateralOutpoint{uint256(), (uint32_t)-1}; // if hash is null, we refer to a ProRegTx output CService addr; @@ -70,7 +71,7 @@ class CProRegTx obj.scriptPayout, obj.inputsHash ); - if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == MnType::HighPerformance.index) { READWRITE( obj.platformNodeID, obj.platformP2PPort, @@ -105,7 +106,7 @@ class CProRegTx } obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION)); obj.pushKV("operatorReward", (double)nOperatorReward / 100); - if (nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (nType == MnType::HighPerformance.index) { obj.pushKV("platformNodeID", platformNodeID.ToString()); obj.pushKV("platformP2PPort", platformP2PPort); obj.pushKV("platformHTTPPort", platformHTTPPort); @@ -122,8 +123,8 @@ class CProUpServTx static constexpr auto SPECIALTX_TYPE = TRANSACTION_PROVIDER_UPDATE_SERVICE; static constexpr uint16_t LEGACY_BLS_VERSION = 1; static constexpr uint16_t BASIC_BLS_VERSION = 2; - static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; - static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; +// static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; +// static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; [[nodiscard]] static constexpr auto GetVersion(const bool is_basic_scheme_active) -> uint16_t { @@ -131,7 +132,7 @@ class CProUpServTx } uint16_t nVersion{LEGACY_BLS_VERSION}; // message version - uint16_t nType{TYPE_REGULAR_MASTERNODE}; + uint16_t nType{MnType::Regular.index}; uint256 proTxHash; CService addr; uint160 platformNodeID{}; @@ -160,7 +161,7 @@ class CProUpServTx obj.scriptOperatorPayout, obj.inputsHash ); - if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (obj.nVersion == BASIC_BLS_VERSION && obj.nType == MnType::HighPerformance.index) { READWRITE( obj.platformNodeID, obj.platformP2PPort, @@ -187,7 +188,7 @@ class CProUpServTx if (ExtractDestination(scriptOperatorPayout, dest)) { obj.pushKV("operatorPayoutAddress", EncodeDestination(dest)); } - if (nType == TYPE_HIGH_PERFORMANCE_MASTERNODE) { + if (nType == MnType::HighPerformance.index) { obj.pushKV("platformNodeID", platformNodeID.ToString()); obj.pushKV("platformP2PPort", platformP2PPort); obj.pushKV("platformHTTPPort", platformHTTPPort); diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index f8ec58bf4f92..97447ef7365d 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -72,7 +72,7 @@ void CSimplifiedMNListEntry::ToJson(UniValue& obj, bool extended) const obj.pushKV("isValid", isValid); obj.pushKV("nVersion", nVersion); obj.pushKV("nType", nType); - if (nType == CDeterministicMN::MasternodeType::HighPerformance) { + if (nType == MnType::HighPerformance.index) { obj.pushKV("platformHTTPPort", platformHTTPPort); obj.pushKV("platformNodeID", platformNodeID.ToString()); } diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 746da90b9475..50bdb8bc3a08 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -10,6 +10,7 @@ #include #include #include +#include class UniValue; class CBlockIndex; @@ -33,7 +34,7 @@ class CSimplifiedMNListEntry CBLSLazyPublicKey pubKeyOperator; CKeyID keyIDVoting; bool isValid{false}; - uint16_t nType{CDeterministicMN::MasternodeType::Regular}; + uint16_t nType{MnType::Regular.index}; uint16_t platformHTTPPort{0}; uint160 platformNodeID{}; CScript scriptPayout; // mem-only @@ -74,7 +75,7 @@ class CSimplifiedMNListEntry ); if (obj.nVersion == BASIC_BLS_VERSION) { READWRITE(obj.nType); - if (obj.nType == CDeterministicMN::MasternodeType::HighPerformance) { + if (obj.nType == MnType::HighPerformance.index) { READWRITE(obj.platformHTTPPort); READWRITE(obj.platformNodeID); } diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 6397c4007f91..a30cc60e46d9 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -631,9 +631,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote // 4x times weight vote for HPMN owners. // No need to check if v19 is active since no HPMN are allowed to register before v19s auto dmn = mnList.GetMNByCollateral(votepair.first); - if (dmn) nCount += (dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT - : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; + if (dmn) nCount += GetMnType(dmn->nType).voting_weight; } } return nCount; diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index ea5ee3cda710..24f5b8601bcb 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -3,6 +3,7 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include @@ -112,9 +113,7 @@ std::string CGovernanceVote::ToString() const { auto mnList = deterministicMNManager->GetListAtChainTip(); auto dmn = mnList.GetMNByCollateral(masternodeOutpoint); - int voteWeight = (dmn != nullptr && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) - ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT - : CDeterministicMN::REGULAR_MASTERNODE_WEIGHT; + int voteWeight = GetMnType(dmn->nType).voting_weight; std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" << nTime << ":" diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 919c4488c27d..c985f4a40661 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -583,7 +584,8 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, } else { ptx.nVersion = CProRegTx::GetVersion(isV19active); } - ptx.nType = isHPMNrequested ? CProRegTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProRegTx::TYPE_REGULAR_MASTERNODE; + auto mn_type = isHPMNrequested ? MnType::HighPerformance : MnType::Regular; + ptx.nType = mn_type.index; if (isFundRegister) { CTxDestination collateralDest = DecodeDestination(request.params[paramIdx].get_str()); @@ -592,7 +594,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, } CScript collateralScript = GetScriptForDestination(collateralDest); - CAmount fundCollateral = isHPMNrequested ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + CAmount fundCollateral = mn_type.collat_amount; CTxOut collateralTxOut(fundCollateral, collateralScript); tx.vout.emplace_back(collateralTxOut); @@ -686,7 +688,7 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, } if (isFundRegister) { - CAmount fundCollateral = isHPMNrequested ? CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL : CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL; + CAmount fundCollateral = mn_type.collat_amount; uint32_t collateralIndex = (uint32_t) -1; for (uint32_t i = 0; i < tx.vout.size(); i++) { if (tx.vout[i].nValue == fundCollateral) { @@ -864,7 +866,7 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques CProUpServTx ptx; ptx.nVersion = CProUpServTx::GetVersion(llmq::utils::IsV19Active(::ChainActive().Tip())); - ptx.nType = isHPMNrequested ? CProUpServTx::TYPE_HIGH_PERFORMANCE_MASTERNODE : CProUpServTx::TYPE_REGULAR_MASTERNODE; + ptx.nType = isHPMNrequested ? MnType::HighPerformance.index : MnType::Regular.index; ptx.proTxHash = ParseHashV(request.params[0], "proTxHash"); if (!Lookup(request.params[1].get_str().c_str(), ptx.addr, Params().GetDefaultPort(), false)) { @@ -899,9 +901,9 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques if (!dmn) { throw std::runtime_error(strprintf("masternode with proTxHash %s not found", ptx.proTxHash.ToString())); } - if (isHPMNrequested && dmn->nType != CDeterministicMN::MasternodeType::HighPerformance) { + if (isHPMNrequested && dmn->nType != MnType::HighPerformance.index) { throw std::runtime_error(strprintf("masternode with proTxHash %s is not a HPMN", ptx.proTxHash.ToString())); - } else if (!isHPMNrequested && dmn->nType == CDeterministicMN::MasternodeType::HighPerformance) { + } else if (!isHPMNrequested && dmn->nType == MnType::HighPerformance.index) { throw std::runtime_error(strprintf("masternode with proTxHash %s is a HPMN", ptx.proTxHash.ToString())); } diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 628fd038ebc9..2884e016a169 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -667,8 +667,8 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("address", dmn.pdmnState->addr.ToString()); objMN.pushKV("payee", payeeStr); objMN.pushKV("status", dmnToStatus(dmn)); - objMN.pushKV("type", dmn.nType == CDeterministicMN::MasternodeType::HighPerformance ? "HighPerformance" : "Regular"); - if (dmn.nType == CDeterministicMN::MasternodeType::HighPerformance) { + objMN.pushKV("type", dmn.nType == MnType::HighPerformance.index ? "HighPerformance" : "Regular"); + if (dmn.nType == MnType::HighPerformance.index) { objMN.pushKV("platformNodeID", dmn.pdmnState->platformNodeID.ToString()); objMN.pushKV("platformP2PPort", dmn.pdmnState->platformP2PPort); objMN.pushKV("platformHTTPPort", dmn.pdmnState->platformHTTPPort); diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp index 6f32f1dcde02..0626ccbec412 100644 --- a/src/test/block_reward_reallocation_tests.cpp +++ b/src/test/block_reward_reallocation_tests.cpp @@ -132,7 +132,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM CMutableTransaction tx; tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL); + FundTransaction(tx, utxos, scriptPayout, MnType::Regular.collat_amount); proTx.inputsHash = CalcTxInputsHash(CTransaction(tx)); SetTxPayload(tx, proTx); SignTransaction(mempool, tx, coinbaseKey); diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index b46ff5f7c636..807028d73229 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -110,7 +110,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM CMutableTransaction tx; tx.nVersion = 3; tx.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, coinbaseKey); + FundTransaction(tx, utxos, scriptPayout, MnType::Regular.collat_amount, coinbaseKey); proTx.inputsHash = CalcTxInputsHash(CTransaction(tx)); SetTxPayload(tx, proTx); SignTransaction(mempool, tx, coinbaseKey); @@ -468,7 +468,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) // Create a MN with an external collateral CMutableTransaction tx_collateral; - FundTransaction(tx_collateral, utxos, scriptCollateral, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); + FundTransaction(tx_collateral, utxos, scriptCollateral, MnType::Regular.collat_amount, setup.coinbaseKey); SignTransaction(*(setup.m_node.mempool), tx_collateral, setup.coinbaseKey); auto block = std::make_shared(setup.CreateBlock({tx_collateral}, setup.coinbaseKey)); @@ -486,7 +486,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) payload.scriptPayout = scriptPayout; for (size_t i = 0; i < tx_collateral.vout.size(); ++i) { - if (tx_collateral.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { + if (tx_collateral.vout[i].nValue == MnType::Regular.collat_amount) { payload.collateralOutpoint = COutPoint(tx_collateral.GetHash(), i); break; } @@ -495,7 +495,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) CMutableTransaction tx_reg; tx_reg.nVersion = 3; tx_reg.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx_reg, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); + FundTransaction(tx_reg, utxos, scriptPayout, MnType::Regular.collat_amount, setup.coinbaseKey); payload.inputsHash = CalcTxInputsHash(CTransaction(tx_reg)); CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey); SetTxPayload(tx_reg, payload); @@ -555,7 +555,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) payload.scriptPayout = scriptPayout; for (size_t i = 0; i < tx_reg1.vout.size(); ++i) { - if (tx_reg1.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { + if (tx_reg1.vout[i].nValue == MnType::Regular.collat_amount) { payload.collateralOutpoint = COutPoint(tx_reg1.GetHash(), i); break; } @@ -564,7 +564,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) CMutableTransaction tx_reg2; tx_reg2.nVersion = 3; tx_reg2.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx_reg2, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); + FundTransaction(tx_reg2, utxos, scriptPayout, MnType::Regular.collat_amount, setup.coinbaseKey); payload.inputsHash = CalcTxInputsHash(CTransaction(tx_reg2)); CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey); SetTxPayload(tx_reg2, payload); @@ -599,7 +599,7 @@ void FuncVerifyDB(TestChainSetup& setup) // Create a MN with an external collateral CMutableTransaction tx_collateral; - FundTransaction(tx_collateral, utxos, scriptCollateral, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); + FundTransaction(tx_collateral, utxos, scriptCollateral, MnType::Regular.collat_amount, setup.coinbaseKey); SignTransaction(*(setup.m_node.mempool), tx_collateral, setup.coinbaseKey); auto block = std::make_shared(setup.CreateBlock({tx_collateral}, setup.coinbaseKey)); @@ -617,7 +617,7 @@ void FuncVerifyDB(TestChainSetup& setup) payload.scriptPayout = scriptPayout; for (size_t i = 0; i < tx_collateral.vout.size(); ++i) { - if (tx_collateral.vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL) { + if (tx_collateral.vout[i].nValue == MnType::Regular.collat_amount) { payload.collateralOutpoint = COutPoint(tx_collateral.GetHash(), i); break; } @@ -626,7 +626,7 @@ void FuncVerifyDB(TestChainSetup& setup) CMutableTransaction tx_reg; tx_reg.nVersion = 3; tx_reg.nType = TRANSACTION_PROVIDER_REGISTER; - FundTransaction(tx_reg, utxos, scriptPayout, CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL, setup.coinbaseKey); + FundTransaction(tx_reg, utxos, scriptPayout, MnType::Regular.collat_amount, setup.coinbaseKey); payload.inputsHash = CalcTxInputsHash(CTransaction(tx_reg)); CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey); SetTxPayload(tx_reg, payload); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e01a9585cad0..f411cd3b62ac 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2535,7 +2535,7 @@ void CWallet::AvailableCoins(std::vector &vCoins, bool fOnlySafe, const if (CCoinJoin::IsCollateralAmount(pcoin->tx->vout[i].nValue)) continue; // do not use collateral amounts found = !CCoinJoin::IsDenominatedAmount(pcoin->tx->vout[i].nValue); } else if(nCoinType == CoinType::ONLY_MASTERNODE_COLLATERAL) { - found = pcoin->tx->vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL || pcoin->tx->vout[i].nValue == CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL; + found = pcoin->tx->vout[i].nValue == MnType::Regular.collat_amount || pcoin->tx->vout[i].nValue == MnType::HighPerformance.collat_amount; } else if(nCoinType == CoinType::ONLY_COINJOIN_COLLATERAL) { found = CCoinJoin::IsCollateralAmount(pcoin->tx->vout[i].nValue); } else { @@ -3046,7 +3046,7 @@ std::vector CWallet::SelectCoinsGroupedByAddresses(bool fSkipD if(fAnonymizable) { // ignore collaterals if(CCoinJoin::IsCollateralAmount(wtx.tx->vout[i].nValue)) continue; - if (fMasternodeMode && (wtx.tx->vout[i].nValue == CDeterministicMN::REGULAR_MASTERNODE_COLLATERAL || wtx.tx->vout[i].nValue == CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_COLLATERAL)) continue; + if (fMasternodeMode && (wtx.tx->vout[i].nValue == MnType::Regular.collat_amount || wtx.tx->vout[i].nValue == MnType::HighPerformance.collat_amount)) continue; // ignore outputs that are 10 times smaller then the smallest denomination // otherwise they will just lead to higher fee / lower priority if(wtx.tx->vout[i].nValue <= nSmallestDenom/10) continue; From 9e51c24fad8de9868bf1c89b7fcff0b47fafbeb2 Mon Sep 17 00:00:00 2001 From: pasta Date: Sun, 12 Feb 2023 13:07:51 -0600 Subject: [PATCH 58/64] add dmn_types.h --- src/evo/dmn_types.h | 59 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/evo/dmn_types.h diff --git a/src/evo/dmn_types.h b/src/evo/dmn_types.h new file mode 100644 index 000000000000..f0041cdf3766 --- /dev/null +++ b/src/evo/dmn_types.h @@ -0,0 +1,59 @@ +// Copyright (c) 2023 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_EVO_DMN_TYPES_H +#define BITCOIN_EVO_DMN_TYPES_H + +#include +#include + +class CDeterministicMNType +{ +public: + uint8_t index; + int32_t voting_weight; + CAmount collat_amount; + +//public: +// constexpr CDeterministicMNType(int32_t vot, CAmount col) { +// voting_weight = vot; +// collat_amount = col; +// } +// +// int32_t getVotingWeight() { +// return voting_weight; +// } +// CAmount getCollateralAmount() { +// return collat_amount; +// } +}; + +namespace MnType { + constexpr auto Regular = CDeterministicMNType{ + .index = 0, + .voting_weight = 1, + .collat_amount = 1000, + }; + constexpr auto HighPerformance = CDeterministicMNType{ + .index = 1, + .voting_weight = 4, + .collat_amount = 4000, + }; +} + +constexpr const auto& GetMnType(int index) { + switch (index) { + case 0: return MnType::Regular; + case 1: return MnType::HighPerformance; + default: assert(false); + } +} + +// Ensure that these are in the order of index +//std::array MnTypes { +// MnType::Regular, +// MnType::HighPerformance +//}; + +#endif //BITCOIN_EVO_DMN_TYPES_H From 2d51efb771e428f0d89fa2e1e597c74c5a06a196 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 13 Feb 2023 12:25:12 +0200 Subject: [PATCH 59/64] adjustements and cleanups Co-Authored-By: PastaPastaPasta <6443210+pastapastapasta@users.noreply.github.com> --- src/evo/deterministicmns.h | 10 ---------- src/evo/dmn_types.h | 23 ++--------------------- src/evo/providertx.h | 4 ---- 3 files changed, 2 insertions(+), 35 deletions(-) diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index d9adbff12752..f2ff18020ca1 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -41,16 +41,6 @@ class CDeterministicMN uint64_t internalId{std::numeric_limits::max()}; public: -// enum MasternodeType : std::uint16_t { -// Regular = 0, -// HighPerformance = 1 -// }; - -// static constexpr CAmount REGULAR_MASTERNODE_WEIGHT{1}; -// static constexpr CAmount REGULAR_MASTERNODE_COLLATERAL{1000 * COIN}; -// static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_WEIGHT{4}; -// static constexpr CAmount HIGH_PERFORMANCE_MASTERNODE_COLLATERAL{REGULAR_MASTERNODE_COLLATERAL * CDeterministicMN::HIGH_PERFORMANCE_MASTERNODE_WEIGHT}; - static constexpr uint16_t CURRENT_MN_FORMAT = 0; static constexpr uint16_t MN_TYPE_FORMAT = 1; diff --git a/src/evo/dmn_types.h b/src/evo/dmn_types.h index f0041cdf3766..b94bcb609a6b 100644 --- a/src/evo/dmn_types.h +++ b/src/evo/dmn_types.h @@ -14,31 +14,18 @@ class CDeterministicMNType uint8_t index; int32_t voting_weight; CAmount collat_amount; - -//public: -// constexpr CDeterministicMNType(int32_t vot, CAmount col) { -// voting_weight = vot; -// collat_amount = col; -// } -// -// int32_t getVotingWeight() { -// return voting_weight; -// } -// CAmount getCollateralAmount() { -// return collat_amount; -// } }; namespace MnType { constexpr auto Regular = CDeterministicMNType{ .index = 0, .voting_weight = 1, - .collat_amount = 1000, + .collat_amount = 1000 * COIN, }; constexpr auto HighPerformance = CDeterministicMNType{ .index = 1, .voting_weight = 4, - .collat_amount = 4000, + .collat_amount = 4000 * COIN, }; } @@ -50,10 +37,4 @@ constexpr const auto& GetMnType(int index) { } } -// Ensure that these are in the order of index -//std::array MnTypes { -// MnType::Regular, -// MnType::HighPerformance -//}; - #endif //BITCOIN_EVO_DMN_TYPES_H diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 26c505697fad..19d332eea743 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -27,8 +27,6 @@ class CProRegTx static constexpr auto SPECIALTX_TYPE = TRANSACTION_PROVIDER_REGISTER; static constexpr uint16_t LEGACY_BLS_VERSION = 1; static constexpr uint16_t BASIC_BLS_VERSION = 2; -// static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; -// static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; [[nodiscard]] static constexpr auto GetVersion(const bool is_basic_scheme_active) -> uint16_t { @@ -123,8 +121,6 @@ class CProUpServTx static constexpr auto SPECIALTX_TYPE = TRANSACTION_PROVIDER_UPDATE_SERVICE; static constexpr uint16_t LEGACY_BLS_VERSION = 1; static constexpr uint16_t BASIC_BLS_VERSION = 2; -// static constexpr uint16_t TYPE_REGULAR_MASTERNODE = 0; -// static constexpr uint16_t TYPE_HIGH_PERFORMANCE_MASTERNODE = 1; [[nodiscard]] static constexpr auto GetVersion(const bool is_basic_scheme_active) -> uint16_t { From d1c1bce9795b97b0f424c776bf71c0356a77645a Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 13 Feb 2023 12:46:53 +0200 Subject: [PATCH 60/64] clang formater --- src/evo/deterministicmns.cpp | 6 +++--- src/evo/deterministicmns.h | 4 ++-- src/evo/dmn_types.h | 33 +++++++++++++++++---------------- src/evo/providertx.cpp | 2 +- src/evo/providertx.h | 2 +- src/evo/simplifiedmns.h | 2 +- src/governance/vote.cpp | 2 +- src/rpc/evo.cpp | 4 ++-- 8 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 868f275060c4..30dd31cf35c8 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -3,13 +3,13 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include -#include +#include #include -#include +#include #include #include -#include #include #include diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index f2ff18020ca1..5e4dfd0cd340 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -8,11 +8,11 @@ #include #include -#include #include +#include +#include #include #include -#include #include #include #include diff --git a/src/evo/dmn_types.h b/src/evo/dmn_types.h index b94bcb609a6b..fa4ec45cf354 100644 --- a/src/evo/dmn_types.h +++ b/src/evo/dmn_types.h @@ -17,24 +17,25 @@ class CDeterministicMNType }; namespace MnType { - constexpr auto Regular = CDeterministicMNType{ - .index = 0, - .voting_weight = 1, - .collat_amount = 1000 * COIN, - }; - constexpr auto HighPerformance = CDeterministicMNType{ - .index = 1, - .voting_weight = 4, - .collat_amount = 4000 * COIN, - }; -} +constexpr auto Regular = CDeterministicMNType{ + .index = 0, + .voting_weight = 1, + .collat_amount = 1000 * COIN, +}; +constexpr auto HighPerformance = CDeterministicMNType{ + .index = 1, + .voting_weight = 4, + .collat_amount = 4000 * COIN, +}; +} // namespace MnType -constexpr const auto& GetMnType(int index) { +constexpr const auto& GetMnType(int index) +{ switch (index) { - case 0: return MnType::Regular; - case 1: return MnType::HighPerformance; - default: assert(false); + case 0: return MnType::Regular; + case 1: return MnType::HighPerformance; + default: assert(false); } } -#endif //BITCOIN_EVO_DMN_TYPES_H +#endif // BITCOIN_EVO_DMN_TYPES_H diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 54c67321341a..4d1139f9a2ab 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include +#include #include #include diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 19d332eea743..01b01dac34e4 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -10,12 +10,12 @@ #include #include +#include #include #include #include #include #include -#include class CBlockIndex; class CCoinsViewCache; diff --git a/src/evo/simplifiedmns.h b/src/evo/simplifiedmns.h index 50bdb8bc3a08..9cb9718932ab 100644 --- a/src/evo/simplifiedmns.h +++ b/src/evo/simplifiedmns.h @@ -7,10 +7,10 @@ #include #include +#include #include #include #include -#include class UniValue; class CBlockIndex; diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 24f5b8601bcb..1a4b16b526c0 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include +#include #include #include diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index c985f4a40661..e002cd2f4cf4 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -8,11 +8,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -24,8 +24,8 @@ #include #include #include -#include #include +#include #include #ifdef ENABLE_WALLET From 7281e2103b79b814f5f6ff9a251f2170ab03ebf6 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 13 Feb 2023 18:20:25 +0200 Subject: [PATCH 61/64] Apply suggestions from code review Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> --- src/llmq/utils.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/llmq/utils.h b/src/llmq/utils.h index 9aa1eacba108..587781336865 100644 --- a/src/llmq/utils.h +++ b/src/llmq/utils.h @@ -85,7 +85,6 @@ Consensus::LLMQType GetInstantSendLLMQType(bool deterministic); bool IsDIP0024Active(const CBlockIndex* pindex); bool IsV19Active(const CBlockIndex* pindex); const CBlockIndex* V19ActivationIndex(const CBlockIndex* pindex); -static bool IsInstantSendLLMQTypeShared(); static bool IsLLMQTypeHPMNOnly(Consensus::LLMQType llmqType); /// Returns the state of `-llmq-data-recovery` From f7ab2913ae1b6ac87a694bcc2435f674dcd9019b Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Mon, 13 Feb 2023 18:36:09 +0200 Subject: [PATCH 62/64] check dmn --- src/governance/vote.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 1a4b16b526c0..7d143b565457 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -113,7 +113,7 @@ std::string CGovernanceVote::ToString() const { auto mnList = deterministicMNManager->GetListAtChainTip(); auto dmn = mnList.GetMNByCollateral(masternodeOutpoint); - int voteWeight = GetMnType(dmn->nType).voting_weight; + int voteWeight = dmn ? GetMnType(dmn->nType).voting_weight : 0; std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" << nTime << ":" From 45849e3446ac8115aa7267c6e0b09f96c5b89a25 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 14 Feb 2023 12:38:16 +0200 Subject: [PATCH 63/64] various refactorin Co-Authored-By: UdjinM6 <1935069+Udjinm6@users.noreply.github.com> --- src/evo/deterministicmns.cpp | 16 ++- src/evo/dmnstate.h | 2 +- src/evo/providertx.h | 5 +- src/governance/object.cpp | 2 +- src/governance/vote.cpp | 2 +- src/rpc/evo.cpp | 191 +++++++++++++++------------ test/functional/feature_llmq_hpmn.py | 9 +- 7 files changed, 122 insertions(+), 105 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 30dd31cf35c8..ba50ea0fddb0 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -196,18 +196,16 @@ CDeterministicMNCPtr CDeterministicMNList::GetMNPayee(const CBlockIndex* pIndex) best = dmn; } } - return; }); - if (best) - return best; + if (best != nullptr) return best; // Note: If the last payee was a regular MN or if the payee is a HPMN that was removed from the mnList then that's fine. // We can proceed with classic MN payee selection } ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { - if (!best || CompareByLastPaid(dmn.get(), best.get())) { + if (best == nullptr || CompareByLastPaid(dmn.get(), best.get())) { best = dmn; } }); @@ -956,8 +954,8 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C } } newList.UpdateMN(payee->proTxHash, newState); - dmn = newList.GetMN(payee->proTxHash); if (debugLogs) { + dmn = newList.GetMN(payee->proTxHash); LogPrintf("CDeterministicMNManager::%s -- MN %s, nConsecutivePayments=%d\n", __func__, dmn->proTxHash.ToString(), dmn->pdmnState->nConsecutivePayments); } @@ -1333,6 +1331,14 @@ static bool CheckPlatformFields(const ProTx& proTx, CValidationState& state) } } + static int mainnetDefaultP2PPort = CreateChainParams(CBaseChainParams::MAIN)->GetDefaultPort(); + if (proTx.platformP2PPort == mainnetDefaultP2PPort) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-p2p-port"); + } + if (proTx.platformHTTPPort == mainnetDefaultP2PPort) { + return state.Invalid(ValidationInvalidReason::TX_BAD_SPECIAL, false, REJECT_INVALID, "bad-protx-platform-http-port"); + } + if (proTx.platformP2PPort == proTx.platformHTTPPort || proTx.platformP2PPort == proTx.addr.GetPort() || proTx.platformHTTPPort == proTx.addr.GetPort()) { diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index 2e41cf16ea4d..e772a4e89226 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -198,7 +198,6 @@ class CDeterministicMNState h.Finalize(confirmedHashWithProRegTxHash.begin()); } - public: std::string ToString() const; void ToJson(UniValue& obj) const; @@ -283,4 +282,5 @@ class CDeterministicMNStateDiff } }; + #endif //BITCOIN_EVO_DMNSTATE_H diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 01b01dac34e4..341ac1a12ce3 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -52,13 +52,14 @@ class CProRegTx SERIALIZE_METHODS(CProRegTx, obj) { READWRITE( - obj.nVersion, - obj.nType); + obj.nVersion + ); if (obj.nVersion == 0 || obj.nVersion > BASIC_BLS_VERSION) { // unknown version, bail out early return; } READWRITE( + obj.nType, obj.nMode, obj.collateralOutpoint, obj.addr, diff --git a/src/governance/object.cpp b/src/governance/object.cpp index a30cc60e46d9..5bb57ff08bae 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -631,7 +631,7 @@ int CGovernanceObject::CountMatchingVotes(vote_signal_enum_t eVoteSignalIn, vote // 4x times weight vote for HPMN owners. // No need to check if v19 is active since no HPMN are allowed to register before v19s auto dmn = mnList.GetMNByCollateral(votepair.first); - if (dmn) nCount += GetMnType(dmn->nType).voting_weight; + if (dmn != nullptr) nCount += GetMnType(dmn->nType).voting_weight; } } return nCount; diff --git a/src/governance/vote.cpp b/src/governance/vote.cpp index 7d143b565457..f5dd9beed9a1 100644 --- a/src/governance/vote.cpp +++ b/src/governance/vote.cpp @@ -113,7 +113,7 @@ std::string CGovernanceVote::ToString() const { auto mnList = deterministicMNManager->GetListAtChainTip(); auto dmn = mnList.GetMNByCollateral(masternodeOutpoint); - int voteWeight = dmn ? GetMnType(dmn->nType).voting_weight : 0; + int voteWeight = dmn != nullptr ? GetMnType(dmn->nType).voting_weight : 0; std::ostringstream ostr; ostr << masternodeOutpoint.ToStringShort() << ":" << nTime << ":" diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index e002cd2f4cf4..1fe27e0e99c0 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -45,90 +45,112 @@ static RPCArg GetRpcArg(const std::string& strParamName) { static const std::map mapParamHelp = { {"collateralAddress", - {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to send the collateral to."}}, + {"collateralAddress", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to send the collateral to."} + }, {"collateralHash", - {"collateralHash", RPCArg::Type::STR, RPCArg::Optional::NO, - "The collateral transaction hash."}}, + {"collateralHash", RPCArg::Type::STR, RPCArg::Optional::NO, + "The collateral transaction hash."} + }, {"collateralIndex", - {"collateralIndex", RPCArg::Type::NUM, RPCArg::Optional::NO, - "The collateral transaction output index."}}, + {"collateralIndex", RPCArg::Type::NUM, RPCArg::Optional::NO, + "The collateral transaction output index."} + }, {"feeSourceAddress", - {"feeSourceAddress", RPCArg::Type::STR, /* default */ "", - "If specified wallet will only use coins from this address to fund ProTx.\n" - "If not specified, payoutAddress is the one that is going to be used.\n" - "The private key belonging to this address must be known in your wallet."}}, + {"feeSourceAddress", RPCArg::Type::STR, /* default */ "", + "If specified wallet will only use coins from this address to fund ProTx.\n" + "If not specified, payoutAddress is the one that is going to be used.\n" + "The private key belonging to this address must be known in your wallet."} + }, {"fundAddress", - {"fundAddress", RPCArg::Type::STR, /* default */ "", - "If specified wallet will only use coins from this address to fund ProTx.\n" - "If not specified, payoutAddress is the one that is going to be used.\n" - "The private key belonging to this address must be known in your wallet."}}, + {"fundAddress", RPCArg::Type::STR, /* default */ "", + "If specified wallet will only use coins from this address to fund ProTx.\n" + "If not specified, payoutAddress is the one that is going to be used.\n" + "The private key belonging to this address must be known in your wallet."} + }, {"ipAndPort", - {"ipAndPort", RPCArg::Type::STR, RPCArg::Optional::NO, - "IP and port in the form \"IP:PORT\".\n" - "Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards."}}, + {"ipAndPort", RPCArg::Type::STR, RPCArg::Optional::NO, + "IP and port in the form \"IP:PORT\".\n" + "Must be unique on the network. Can be set to 0, which will require a ProUpServTx afterwards."} + }, {"operatorKey", - {"operatorKey", RPCArg::Type::STR, RPCArg::Optional::NO, - "The operator BLS private key associated with the\n" - "registered operator public key."}}, + {"operatorKey", RPCArg::Type::STR, RPCArg::Optional::NO, + "The operator BLS private key associated with the\n" + "registered operator public key."} + }, {"operatorPayoutAddress", - {"operatorPayoutAddress", RPCArg::Type::STR, /* default */ "", - "The address used for operator reward payments.\n" - "Only allowed when the ProRegTx had a non-zero operatorReward value.\n" - "If set to an empty string, the currently active payout address is reused."}}, + {"operatorPayoutAddress", RPCArg::Type::STR, /* default */ "", + "The address used for operator reward payments.\n" + "Only allowed when the ProRegTx had a non-zero operatorReward value.\n" + "If set to an empty string, the currently active payout address is reused."} + }, {"operatorPubKey_register", - {"operatorPubKey_register", RPCArg::Type::STR, RPCArg::Optional::NO, - "The operator BLS public key. The BLS private key does not have to be known.\n" - "It has to match the BLS private key which is later used when operating the masternode."}}, + {"operatorPubKey_register", RPCArg::Type::STR, RPCArg::Optional::NO, + "The operator BLS public key. The BLS private key does not have to be known.\n" + "It has to match the BLS private key which is later used when operating the masternode."} + }, {"operatorPubKey_update", - {"operatorPubKey_update", RPCArg::Type::STR, RPCArg::Optional::NO, - "The operator BLS public key. The BLS private key does not have to be known.\n" - "It has to match the BLS private key which is later used when operating the masternode.\n" - "If set to an empty string, the currently active operator BLS public key is reused."}}, + {"operatorPubKey_update", RPCArg::Type::STR, RPCArg::Optional::NO, + "The operator BLS public key. The BLS private key does not have to be known.\n" + "It has to match the BLS private key which is later used when operating the masternode.\n" + "If set to an empty string, the currently active operator BLS public key is reused."} + }, {"operatorReward", - {"operatorReward", RPCArg::Type::STR, RPCArg::Optional::NO, - "The fraction in %% to share with the operator. The value must be\n" - "between 0.00 and 100.00."}}, + {"operatorReward", RPCArg::Type::STR, RPCArg::Optional::NO, + "The fraction in %% to share with the operator. The value must be\n" + "between 0.00 and 100.00."} + }, {"ownerAddress", - {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to use for payee updates and proposal voting.\n" - "The corresponding private key does not have to be known by your wallet.\n" - "The address must be unused and must differ from the collateralAddress."}}, + {"ownerAddress", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to use for payee updates and proposal voting.\n" + "The corresponding private key does not have to be known by your wallet.\n" + "The address must be unused and must differ from the collateralAddress."} + }, {"payoutAddress_register", - {"payoutAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to use for masternode reward payments."}}, + {"payoutAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to use for masternode reward payments."} + }, {"payoutAddress_update", - {"payoutAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, - "The dash address to use for masternode reward payments.\n" - "If set to an empty string, the currently active payout address is reused."}}, + {"payoutAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, + "The dash address to use for masternode reward payments.\n" + "If set to an empty string, the currently active payout address is reused."} + }, {"proTxHash", - {"proTxHash", RPCArg::Type::STR, RPCArg::Optional::NO, - "The hash of the initial ProRegTx."}}, + {"proTxHash", RPCArg::Type::STR, RPCArg::Optional::NO, + "The hash of the initial ProRegTx."} + }, {"reason", - {"reason", RPCArg::Type::NUM, /* default */ "", - "The reason for masternode service revocation."}}, + {"reason", RPCArg::Type::NUM, /* default */ "", + "The reason for masternode service revocation."} + }, {"submit", - {"submit", RPCArg::Type::BOOL, /* default */ "true", - "If true, the resulting transaction is sent to the network."}}, + {"submit", RPCArg::Type::BOOL, /* default */ "true", + "If true, the resulting transaction is sent to the network."} + }, {"votingAddress_register", - {"votingAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, - "The voting key address. The private key does not have to be known by your wallet.\n" - "It has to match the private key which is later used when voting on proposals.\n" - "If set to an empty string, ownerAddress will be used."}}, + {"votingAddress_register", RPCArg::Type::STR, RPCArg::Optional::NO, + "The voting key address. The private key does not have to be known by your wallet.\n" + "It has to match the private key which is later used when voting on proposals.\n" + "If set to an empty string, ownerAddress will be used."} + }, {"votingAddress_update", - {"votingAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, - "The voting key address. The private key does not have to be known by your wallet.\n" - "It has to match the private key which is later used when voting on proposals.\n" - "If set to an empty string, the currently active voting key address is reused."}}, + {"votingAddress_update", RPCArg::Type::STR, RPCArg::Optional::NO, + "The voting key address. The private key does not have to be known by your wallet.\n" + "It has to match the private key which is later used when voting on proposals.\n" + "If set to an empty string, the currently active voting key address is reused."} + }, {"platformNodeID", - {"platformNodeID", RPCArg::Type::STR, RPCArg::Optional::NO, - "Platform P2P node ID, derived from P2P public key."}}, + {"platformNodeID", RPCArg::Type::STR, RPCArg::Optional::NO, + "Platform P2P node ID, derived from P2P public key."} + }, {"platformP2PPort", - {"platformP2PPort", RPCArg::Type::NUM, RPCArg::Optional::NO, - "TCP port of Dash Platform peer-to-peer communication between nodes (network byte order)."}}, + {"platformP2PPort", RPCArg::Type::NUM, RPCArg::Optional::NO, + "TCP port of Dash Platform peer-to-peer communication between nodes (network byte order)."} + }, {"platformHTTPPort", - {"platformHTTPPort", RPCArg::Type::NUM, RPCArg::Optional::NO, - "TCP port of Platform HTTP/API interface (network byte order). "}}, + {"platformHTTPPort", RPCArg::Type::NUM, RPCArg::Optional::NO, + "TCP port of Platform HTTP/API interface (network byte order)."} + }, }; auto it = mapParamHelp.find(strParamName); @@ -168,11 +190,9 @@ static CBLSSecretKey ParseBLSSecretKey(const std::string& hexKey, const std::str return secKey; } -static bool ValidatePort(const int32_t port) +static bool ValidatePlatformPort(const int32_t port) { - if (port < 1 || port > std::numeric_limits::max()) - return false; - return true; + return port >= 1 && port <= std::numeric_limits::max(); } #ifdef ENABLE_WALLET @@ -464,8 +484,7 @@ static void protx_register_fund_hpmn_help(const JSONRPCRequest& request) }, RPCExamples{ HelpExampleCli("protx", "register_fund_hpmn \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" 1000 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - } - .Check(request); + }.Check(request); } static void protx_register_hpmn_help(const JSONRPCRequest& request) @@ -499,8 +518,7 @@ static void protx_register_hpmn_help(const JSONRPCRequest& request) }, RPCExamples{ HelpExampleCli("protx", "register_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - } - .Check(request); + }.Check(request); } static void protx_register_prepare_hpmn_help(const JSONRPCRequest& request) @@ -531,8 +549,7 @@ static void protx_register_prepare_hpmn_help(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "signMessage", "The string message that needs to be signed with the collateral key"}, }}, RPCExamples{HelpExampleCli("protx", "register_prepare_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" 0 \"1.2.3.4:1234\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" \"93746e8731c57f87f79b3620a7982924e2931717d49540a85864bd543de11c43fb868fd63e501a1db37e19ed59ae6db4\" \"Xt9AMWaYSz7tR7Uo7gzXA3m4QmeWgrR3rr\" 0 \"XrVhS9LogauRJGJu2sHuryjhpuex4RNPSb\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - } - .Check(request); + }.Check(request); } static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, @@ -544,19 +561,19 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, { if (isHPMNrequested) { if (isFundRegister && (request.fHelp || (request.params.size() < 10 || request.params.size() > 12))) { - protx_register_fund_hpmn_help(request); + protx_register_fund_hpmn_help(request); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 11 || request.params.size() > 13))) { - protx_register_hpmn_help(request); + protx_register_hpmn_help(request); } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 11 && request.params.size() != 12))) { - protx_register_prepare_hpmn_help(request); + protx_register_prepare_hpmn_help(request); } } else { if (isFundRegister && (request.fHelp || (request.params.size() < 7 || request.params.size() > 9))) { - protx_register_fund_help(request); + protx_register_fund_help(request); } else if (isExternalRegister && (request.fHelp || (request.params.size() < 8 || request.params.size() > 10))) { - protx_register_help(request); + protx_register_help(request); } else if (isPrepareRegister && (request.fHelp || (request.params.size() != 8 && request.params.size() != 9))) { - protx_register_prepare_help(request); + protx_register_prepare_help(request); } } @@ -649,13 +666,13 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, ptx.platformNodeID.SetHex(request.params[paramIdx + 6].get_str()); int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 7].get_str(), "platformP2PPort"); - if (!ValidatePort(requestedPlatformP2PPort)) { + if (!ValidatePlatformPort(requestedPlatformP2PPort)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); } ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[paramIdx + 8].get_str(), "platformHTTPPort"); - if (!ValidatePort(requestedPlatformHTTPPort)) { + if (!ValidatePlatformPort(requestedPlatformHTTPPort)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); } ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); @@ -842,8 +859,7 @@ static void protx_update_service_hpmn_help(const JSONRPCRequest& request) RPCResult::Type::STR_HEX, "txid", "The transaction id"}, RPCExamples{ HelpExampleCli("protx", "update_service_hpmn \"0123456701234567012345670123456701234567012345670123456701234567\" \"1.2.3.4:1234\" \"5a2e15982e62f1e0b7cf9783c64cf7e3af3f90a52d6c40f6f95d624c0b1621cd\" \"f2dbd9b0a1f541a7c44d34a58674d0262f5feca5\" 22821 22822")}, - } - .Check(request); + }.Check(request); } static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& request, const bool isHPMNrequested) @@ -883,13 +899,13 @@ static UniValue protx_update_service_common_wrapper(const JSONRPCRequest& reques ptx.platformNodeID.SetHex(request.params[paramIdx].get_str()); int32_t requestedPlatformP2PPort = ParseInt32V(request.params[paramIdx + 1].get_str(), "platformP2PPort"); - if (!ValidatePort(requestedPlatformP2PPort)) { + if (!ValidatePlatformPort(requestedPlatformP2PPort)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "platformP2PPort must be a valid port [1-65535]"); } ptx.platformP2PPort = static_cast(requestedPlatformP2PPort); int32_t requestedPlatformHTTPPort = ParseInt32V(request.params[paramIdx + 2].get_str(), "platformHTTPPort"); - if (!ValidatePort(requestedPlatformHTTPPort)) { + if (!ValidatePlatformPort(requestedPlatformHTTPPort)) { throw JSONRPCError(RPC_INVALID_PARAMETER, "platformHTTPPort must be a valid port [1-65535]"); } ptx.platformHTTPPort = static_cast(requestedPlatformHTTPPort); @@ -1471,8 +1487,7 @@ static UniValue protx_diff(const JSONRPCRequest& request) }, RPCResults{}, RPCExamples{""}, - } - .Throw(); + }.Throw(); } static UniValue protx(const JSONRPCRequest& request) @@ -1503,7 +1518,7 @@ static UniValue protx(const JSONRPCRequest& request) return protx_revoke_legacy(new_request); } else #endif - if (command == "protxlist") { + if (command == "protxlist") { return protx_list(new_request); } else if (command == "protxinfo") { return protx_info(new_request); diff --git a/test/functional/feature_llmq_hpmn.py b/test/functional/feature_llmq_hpmn.py index 0b94f6c115bb..98a1d3e6196b 100755 --- a/test/functional/feature_llmq_hpmn.py +++ b/test/functional/feature_llmq_hpmn.py @@ -4,9 +4,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. ''' -feature_llmq_rotation.py +feature_llmq_hpmn.py -Checks LLMQs Quorum Rotation +Checks HPMNs ''' from _decimal import Decimal @@ -19,11 +19,6 @@ ) -def intersection(lst1, lst2): - lst3 = [value for value in lst1 if value in lst2] - return lst3 - - def extract_quorum_members(quorum_info): return [d['proTxHash'] for d in quorum_info["members"]] From 82d9f60d9d470162866f0a25833769a3b30380b7 Mon Sep 17 00:00:00 2001 From: Odysseas Gabrielides Date: Tue, 14 Feb 2023 13:54:26 +0200 Subject: [PATCH 64/64] various refactoring Co-Authored-By: Konstantin Akimov <545784+knst@users.noreply.github.com> --- src/evo/deterministicmns.cpp | 4 ++-- src/evo/deterministicmns.h | 4 ++-- src/evo/dmn_types.h | 4 ++++ src/rpc/masternode.cpp | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index ba50ea0fddb0..f58ef238004d 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -50,7 +50,7 @@ void CDeterministicMN::ToJson(UniValue& obj) const UniValue stateObj; pdmnState->ToJson(stateObj); - obj.pushKV("type", nType == MnType::HighPerformance.index ? "HighPerformance" : "Regular"); + obj.pushKV("type", std::string(GetMnType(nType).description)); obj.pushKV("proTxHash", proTxHash.ToString()); obj.pushKV("collateralHash", collateralOutpoint.hash.ToString()); obj.pushKV("collateralIndex", (int)collateralOutpoint.n); @@ -1116,7 +1116,7 @@ bool CDeterministicMNManager::IsProTxWithCollateral(const CTransactionRef& tx, u return false; } - CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount; + const CAmount expectedCollateral = GetMnType(proTx.nType).collat_amount; if (tx->vout[n].nValue != expectedCollateral) { return false; diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 5e4dfd0cd340..701a07fa3e03 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -46,9 +46,9 @@ class CDeterministicMN CDeterministicMN() = delete; // no default constructor, must specify internalId explicit CDeterministicMN(uint64_t _internalId, bool highPerformanceMasternode = false) : - internalId(_internalId) + internalId(_internalId), + nType(highPerformanceMasternode ? MnType::HighPerformance.index : MnType::Regular.index) { - highPerformanceMasternode ? nType = MnType::HighPerformance.index : nType = MnType::Regular.index; // only non-initial values assert(_internalId != std::numeric_limits::max()); } diff --git a/src/evo/dmn_types.h b/src/evo/dmn_types.h index fa4ec45cf354..b69cb9ced323 100644 --- a/src/evo/dmn_types.h +++ b/src/evo/dmn_types.h @@ -7,6 +7,7 @@ #include #include +#include class CDeterministicMNType { @@ -14,6 +15,7 @@ class CDeterministicMNType uint8_t index; int32_t voting_weight; CAmount collat_amount; + std::string_view description; }; namespace MnType { @@ -21,11 +23,13 @@ constexpr auto Regular = CDeterministicMNType{ .index = 0, .voting_weight = 1, .collat_amount = 1000 * COIN, + .description = "Regular", }; constexpr auto HighPerformance = CDeterministicMNType{ .index = 1, .voting_weight = 4, .collat_amount = 4000 * COIN, + .description = "HighPerformance", }; } // namespace MnType diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 2884e016a169..365f26611252 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -667,7 +667,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("address", dmn.pdmnState->addr.ToString()); objMN.pushKV("payee", payeeStr); objMN.pushKV("status", dmnToStatus(dmn)); - objMN.pushKV("type", dmn.nType == MnType::HighPerformance.index ? "HighPerformance" : "Regular"); + objMN.pushKV("type", std::string(GetMnType(dmn.nType).description)); if (dmn.nType == MnType::HighPerformance.index) { objMN.pushKV("platformNodeID", dmn.pdmnState->platformNodeID.ToString()); objMN.pushKV("platformP2PPort", dmn.pdmnState->platformP2PPort);