From 54fb76f2f12a4a01709748503bfa9475e128ec4e Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Sun, 4 Jun 2023 23:45:56 +0300 Subject: [PATCH] fix: Resolve mainnet v19 fork issues (#5403) ## Issue being fixed or feature implemented same as #5392, alternative solution ~based on #5402 atm, will rebase later~ ## What was done? pls see individual commits ## How Has This Been Tested? reorg mainnet around forkpoint with a patched client (to allow low difficulty), run tests ## Breaking Changes Another evodb migration is required. Going back to an older version or migrating after the fork requires reindexing. ## Checklist: - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ --- src/bls/bls.h | 33 ++-- src/evo/deterministicmns.cpp | 193 +++++++++++++------ src/evo/deterministicmns.h | 53 +++-- src/evo/dmnstate.cpp | 6 +- src/evo/dmnstate.h | 96 ++++++++- src/evo/evodb.h | 3 +- src/evo/providertx.cpp | 8 +- src/evo/providertx.h | 12 +- src/evo/specialtxman.cpp | 19 ++ src/governance/object.cpp | 2 +- src/init.cpp | 22 ++- src/llmq/blockprocessor.cpp | 6 - src/llmq/signing.h | 2 +- src/llmq/signing_shares.cpp | 2 +- src/masternode/node.cpp | 1 + src/masternode/node.h | 1 + src/rpc/evo.cpp | 7 +- src/rpc/governance.cpp | 2 +- src/rpc/masternode.cpp | 6 +- src/rpc/quorums.cpp | 2 +- src/test/block_reward_reallocation_tests.cpp | 2 +- src/test/evo_deterministicmns_tests.cpp | 148 +++++++++++++- src/test/evo_simplifiedmns_tests.cpp | 2 +- src/test/util/setup_common.cpp | 7 + src/test/util/setup_common.h | 5 + src/txmempool.cpp | 7 +- src/txmempool.h | 5 +- src/validation.cpp | 8 + test/functional/feature_dip3_v19.py | 9 + 29 files changed, 520 insertions(+), 149 deletions(-) diff --git a/src/bls/bls.h b/src/bls/bls.h index b25563f818..95cd7c197e 100644 --- a/src/bls/bls.h +++ b/src/bls/bls.h @@ -418,10 +418,7 @@ public: CBLSLazyWrapper() : vecBytes(BLSObject::SerSize, 0), bufLegacyScheme(bls::bls_legacy_scheme.load()) - { - // the all-zero buf is considered a valid buf, but the resulting object will return false for IsValid - bufValid = true; - } + {} explicit CBLSLazyWrapper(const CBLSLazyWrapper& r) { @@ -459,12 +456,8 @@ public: { std::unique_lock l(mutex); if (!objInitialized && !bufValid) { - // the all-zero buf is considered a valid buf std::fill(vecBytes.begin(), vecBytes.end(), 0); - bufLegacyScheme = specificLegacyScheme; - bufValid = true; - } - if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) { + } else if (!bufValid || (bufLegacyScheme != specificLegacyScheme)) { vecBytes = obj.ToByteVector(specificLegacyScheme); bufValid = true; bufLegacyScheme = specificLegacyScheme; @@ -476,7 +469,7 @@ public: template inline void Serialize(Stream& s) const { - Serialize(s, bls::bls_legacy_scheme.load()); + Serialize(s, bufLegacyScheme); } template @@ -493,13 +486,14 @@ public: template inline void Unserialize(Stream& s) const { - Unserialize(s, bls::bls_legacy_scheme.load()); + Unserialize(s, bufLegacyScheme); } - void Set(const BLSObject& _obj) + void Set(const BLSObject& _obj, const bool specificLegacyScheme) { std::unique_lock l(mutex); bufValid = false; + bufLegacyScheme = specificLegacyScheme; objInitialized = true; obj = _obj; hash.SetNull(); @@ -549,13 +543,15 @@ public: return !(*this == r); } - uint256 GetHash(const bool specificLegacyScheme = bls::bls_legacy_scheme.load()) const + uint256 GetHash() const { std::unique_lock l(mutex); - if (!bufValid || bufLegacyScheme != specificLegacyScheme) { - vecBytes = obj.ToByteVector(specificLegacyScheme); + if (!objInitialized && !bufValid) { + std::fill(vecBytes.begin(), vecBytes.end(), 0); + hash.SetNull(); + } else if (!bufValid) { + vecBytes = obj.ToByteVector(bufLegacyScheme); bufValid = true; - bufLegacyScheme = specificLegacyScheme; hash.SetNull(); } if (hash.IsNull()) { @@ -565,6 +561,11 @@ public: } return hash; } + + std::string ToString() const + { + return Get().ToString(bufLegacyScheme); + } }; using CBLSLazySignature = CBLSLazyWrapper; using CBLSLazyPublicKey = CBLSLazyWrapper; diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 15da9574c7..6cc146f2de 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -24,8 +24,8 @@ #include -static const std::string DB_LIST_SNAPSHOT = "dmn_S2"; -static const std::string DB_LIST_DIFF = "dmn_D2"; +static const std::string DB_LIST_SNAPSHOT = "dmn_S3"; +static const std::string DB_LIST_DIFF = "dmn_D3"; std::unique_ptr deterministicMNManager; @@ -426,48 +426,6 @@ CDeterministicMNList CDeterministicMNList::ApplyDiff(const CBlockIndex* pindex, return result; } -// RepopulateUniquePropertyMap clears internal mnUniquePropertyMap, and repopulate it with currently MNs unique properties. -// This is needed when the v19 fork activates, we need to store again pubKeyOperator in the mnUniquePropertyMap. -// pubKeyOperator don't differ between the two schemes (legacy and basic(v19)) but their serialisation do: hence their hash. -// And because mnUniquePropertyMap store only hashes, then we need to re-calculate hashes and repopulate. -void CDeterministicMNList::RepopulateUniquePropertyMap() { - decltype(mnUniquePropertyMap) mnUniquePropertyMapEmpty; - mnUniquePropertyMap = mnUniquePropertyMapEmpty; - - for (const auto &p: mnMap) { - auto dmn = p.second; - if (!AddUniqueProperty(*dmn, dmn->collateralOutpoint)) { - throw (std::runtime_error( - strprintf("%s: Can't add a masternode %s with a duplicate collateralOutpoint=%s", __func__, - dmn->proTxHash.ToString(), dmn->collateralOutpoint.ToStringShort()))); - } - if (dmn->pdmnState->addr != CService() && !AddUniqueProperty(*dmn, dmn->pdmnState->addr)) { - throw (std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate address=%s", __func__, - dmn->proTxHash.ToString(), - dmn->pdmnState->addr.ToStringIPPort(false)))); - } - if (!AddUniqueProperty(*dmn, dmn->pdmnState->keyIDOwner)) { - throw (std::runtime_error( - strprintf("%s: Can't add a masternode %s with a duplicate keyIDOwner=%s", __func__, - dmn->proTxHash.ToString(), EncodeDestination(PKHash(dmn->pdmnState->keyIDOwner))))); - } - if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && - !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) { - throw (std::runtime_error( - strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__, - dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); - } - - if (dmn->nType == MnType::HighPerformance) { - if (!AddUniqueProperty(*dmn, dmn->pdmnState->platformNodeID)) { - 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()))); - } - } - } -} - void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount) { assert(dmn != nullptr); @@ -501,7 +459,7 @@ void CDeterministicMNList::AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTota if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !AddUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't add a masternode %s with a duplicate pubKeyOperator=%s", __func__, - dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); + dmn->proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString()))); } if (dmn->nType == MnType::HighPerformance) { @@ -543,7 +501,7 @@ void CDeterministicMNList::UpdateMN(const CDeterministicMN& oldDmn, const std::s if (!UpdateUniqueProperty(*dmn, oldState->pubKeyOperator, pdmnState->pubKeyOperator)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; 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()))); + oldDmn.proTxHash.ToString(), pdmnState->pubKeyOperator.ToString()))); } if (dmn->nType == MnType::HighPerformance) { if (!UpdateUniqueProperty(*dmn, oldState->platformNodeID, dmn->pdmnState->platformNodeID)) { @@ -602,7 +560,7 @@ void CDeterministicMNList::RemoveMN(const uint256& proTxHash) if (dmn->pdmnState->pubKeyOperator.Get().IsValid() && !DeleteUniqueProperty(*dmn, dmn->pdmnState->pubKeyOperator)) { mnUniquePropertyMap = mnUniquePropertyMapSaved; throw(std::runtime_error(strprintf("%s: Can't delete a masternode %s with a pubKeyOperator=%s", __func__, - proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.Get().ToString()))); + proTxHash.ToString(), dmn->pdmnState->pubKeyOperator.ToString()))); } if (dmn->nType == MnType::HighPerformance) { @@ -650,17 +608,14 @@ bool CDeterministicMNManager::ProcessBlock(const CBlock& block, const CBlockInde newList.SetBlockHash(block.GetHash()); - // If the fork is active for pindex block, then we need to repopulate property map - // (Check documentation of CDeterministicMNList::RepopulateUniquePropertyMap()). - // This is needed only when base list is pre-v19 fork and pindex is post-v19 fork. - bool v19_just_activated = pindex == llmq::utils::V19ActivationIndex(pindex); - if (v19_just_activated) { - newList.RepopulateUniquePropertyMap(); - } - oldList = GetListForBlockInternal(pindex->pprev); diff = oldList.BuildDiff(newList); + // NOTE: The block next to the activation is the one that is using new rules. + // If the fork was activated at pindex->prev block then current one is the first one + // using new rules. Save mn list snapsot for this block. + bool v19_just_activated = pindex->pprev == llmq::utils::V19ActivationIndex(pindex); + m_evoDb.Write(std::make_pair(DB_LIST_DIFF, newList.GetBlockHash()), diff); if ((nHeight % DISK_SNAPSHOT_PERIOD) == 0 || oldList.GetHeight() == -1 || v19_just_activated) { m_evoDb.Write(std::make_pair(DB_LIST_SNAPSHOT, newList.GetBlockHash()), newList); @@ -903,12 +858,12 @@ bool CDeterministicMNManager::BuildNewListFromBlock(const CBlock& block, const C return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "bad-protx-hash"); } auto newState = std::make_shared(*dmn->pdmnState); - if (newState->pubKeyOperator.Get() != proTx.pubKeyOperator) { + if (newState->pubKeyOperator != proTx.pubKeyOperator) { // reset all operator related fields and put MN into PoSe-banned state in case the operator key changes newState->ResetOperatorFields(); newState->BanIfNotBanned(nHeight); } - newState->pubKeyOperator.Set(proTx.pubKeyOperator); + newState->pubKeyOperator = proTx.pubKeyOperator; newState->keyIDVoting = proTx.keyIDVoting; newState->scriptPayout = proTx.scriptPayout; @@ -1228,6 +1183,7 @@ 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"; + static const std::string DB_OLD_BEST_BLOCK2 = "b_b3"; LOCK(cs_main); @@ -1239,11 +1195,17 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() return m_evoDb.IsEmpty(); } - if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK)) { + if (m_evoDb.GetRawDB().Exists(EVODB_BEST_BLOCK) || m_evoDb.GetRawDB().Exists(DB_OLD_BEST_BLOCK2)) { LogPrintf("CDeterministicMNManager::%s -- migration already done. skipping.\n", __func__); return true; } + if (::ChainActive().Tip()->pprev != nullptr && llmq::utils::IsV19Active(::ChainActive().Tip()->pprev)) { + // too late + LogPrintf("CDeterministicMNManager::%s -- migration is not possible\n", __func__); + return false; + } + // 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. @@ -1266,15 +1228,15 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) { auto pindex = ::ChainActive()[nHeight]; - // Unserialise CDeterministicMNListDiff 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. + // Unserialise CDeterministicMNListDiff using MN_OLD_FORMAT and set it's type to the default value TYPE_REGULAR_MASTERNODE + // It will be later written with format MN_CURRENT_FORMAT which includes the type field and MN state bls version. CDataStream diff_data(SER_DISK, CLIENT_VERSION); if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindex->GetBlockHash()), diff_data)) { LogPrintf("CDeterministicMNManager::%s -- missing CDeterministicMNListDiff at height %d\n", __func__, nHeight); return false; } CDeterministicMNListDiff mndiff; - mndiff.Unserialize(diff_data, CDeterministicMN::CURRENT_MN_FORMAT); + mndiff.Unserialize(diff_data, CDeterministicMN::MN_OLD_FORMAT); batch.Write(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), mndiff); CDataStream snapshot_data(SER_DISK, CLIENT_VERSION); if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot_data)) { @@ -1282,7 +1244,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() continue; } CDeterministicMNList mnList; - mnList.Unserialize(snapshot_data, CDeterministicMN::CURRENT_MN_FORMAT); + mnList.Unserialize(snapshot_data, CDeterministicMN::MN_OLD_FORMAT); batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), mnList); m_evoDb.GetRawDB().WriteBatch(batch); batch.Clear(); @@ -1291,7 +1253,7 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() m_evoDb.GetRawDB().WriteBatch(batch); - // Writing EVODB_BEST_BLOCK (which is b_b3 now) marks the DB as upgraded + // Writing EVODB_BEST_BLOCK (which is b_b4 now) marks the DB as upgraded auto dbTx = m_evoDb.BeginTransaction(); m_evoDb.WriteBestBlock(::ChainActive().Tip()->GetBlockHash()); dbTx->Commit(); @@ -1307,6 +1269,111 @@ bool CDeterministicMNManager::MigrateDBIfNeeded() LogPrintf("CDeterministicMNManager::%s -- done compacting database\n", __func__); + // flush it to disk + if (!m_evoDb.CommitRootTransaction()) { + LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__); + return false; + } + + return true; +} + +bool CDeterministicMNManager::MigrateDBIfNeeded2() +{ + static const std::string DB_OLD_LIST_SNAPSHOT = "dmn_S2"; + static const std::string DB_OLD_LIST_DIFF = "dmn_D2"; + static const std::string DB_OLD_BEST_BLOCK = "b_b3"; + + LOCK(cs_main); + + LogPrintf("CDeterministicMNManager::%s -- upgrading DB to migrate MN state bls version\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; + } + + if (::ChainActive().Tip()->pprev != nullptr && llmq::utils::IsV19Active(::ChainActive().Tip()->pprev)) { + // too late + LogPrintf("CDeterministicMNManager::%s -- migration is not possible\n", __func__); + return false; + } + + // 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()); + + for (const auto nHeight : irange::range(Params().GetConsensus().DIP0003Height, ::ChainActive().Height() + 1)) { + auto pindex = ::ChainActive()[nHeight]; + // Unserialise CDeterministicMNListDiff using MN_TYPE_FORMAT and set MN state bls version to LEGACY_BLS_VERSION. + // It will be later written with format MN_CURRENT_FORMAT which includes the type field. + CDataStream diff_data(SER_DISK, CLIENT_VERSION); + if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_DIFF, pindex->GetBlockHash()), diff_data)) { + LogPrintf("CDeterministicMNManager::%s -- missing CDeterministicMNListDiff at height %d\n", __func__, nHeight); + return false; + } + CDeterministicMNListDiff mndiff; + mndiff.Unserialize(diff_data, CDeterministicMN::MN_TYPE_FORMAT); + batch.Write(std::make_pair(DB_LIST_DIFF, pindex->GetBlockHash()), mndiff); + CDataStream snapshot_data(SER_DISK, CLIENT_VERSION); + if (!m_evoDb.GetRawDB().ReadDataStream(std::make_pair(DB_OLD_LIST_SNAPSHOT, pindex->GetBlockHash()), snapshot_data)) { + // it's ok, we write snapshots every DISK_SNAPSHOT_PERIOD blocks only + continue; + } + CDeterministicMNList mnList; + mnList.Unserialize(snapshot_data, CDeterministicMN::MN_TYPE_FORMAT); + batch.Write(std::make_pair(DB_LIST_SNAPSHOT, pindex->GetBlockHash()), mnList); + m_evoDb.GetRawDB().WriteBatch(batch); + batch.Clear(); + LogPrintf("CDeterministicMNManager::%s -- wrote snapshot at height %d\n", __func__, nHeight); + } + + m_evoDb.GetRawDB().WriteBatch(batch); + + // Writing EVODB_BEST_BLOCK (which is b_b4 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__); + + // flush it to disk + if (!m_evoDb.CommitRootTransaction()) { + LogPrintf("CDeterministicMNManager::%s -- failed to commit to evoDB\n", __func__); + return false; + } + return true; } diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index 17d2dcc8c2..358df1196a 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -42,8 +42,10 @@ private: uint64_t internalId{std::numeric_limits::max()}; public: - static constexpr uint16_t CURRENT_MN_FORMAT = 0; + static constexpr uint16_t MN_OLD_FORMAT = 0; static constexpr uint16_t MN_TYPE_FORMAT = 1; + static constexpr uint16_t MN_VERSION_FORMAT = 2; + static constexpr uint16_t MN_CURRENT_FORMAT = MN_VERSION_FORMAT; uint256 proTxHash; COutPoint collateralOutpoint; @@ -73,12 +75,16 @@ public: READWRITE(VARINT(internalId)); READWRITE(collateralOutpoint); READWRITE(nOperatorReward); - // We need to read CDeterministicMNState using the old format only when called with CURRENT_MN_FORMAT on Unserialize() + // We need to read CDeterministicMNState using the old format only when called with MN_OLD_FORMAT or MN_TYPE_FORMAT on Unserialize() // Serialisation (writing) will be done always using new format - if (ser_action.ForRead() && format_version == CURRENT_MN_FORMAT) { + if (ser_action.ForRead() && format_version == MN_OLD_FORMAT) { CDeterministicMNState_Oldformat old_state; READWRITE(old_state); pdmnState = std::make_shared(old_state); + } else if (ser_action.ForRead() && format_version == MN_TYPE_FORMAT) { + CDeterministicMNState_mntype_format old_state; + READWRITE(old_state); + pdmnState = std::make_shared(old_state); } else { READWRITE(pdmnState); } @@ -97,11 +103,11 @@ public: template void Serialize(Stream& s) const { - const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_TYPE_FORMAT); + const_cast(this)->SerializationOp(s, CSerActionSerialize(), MN_CURRENT_FORMAT); } template - void Unserialize(Stream& s, const uint8_t format_version = MN_TYPE_FORMAT) + void Unserialize(Stream& s, const uint8_t format_version = MN_CURRENT_FORMAT) { SerializationOp(s, CSerActionUnserialize(), format_version); } @@ -203,14 +209,14 @@ public: } template - void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT) { + void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT) { mnMap = MnMap(); mnUniquePropertyMap = MnUniquePropertyMap(); mnInternalIdMap = MnInternalIdMap(); SerializationOpBase(s, CSerActionUnserialize()); - bool evodb_migration = (format_version == CDeterministicMN::CURRENT_MN_FORMAT); + bool evodb_migration = (format_version == CDeterministicMN::MN_OLD_FORMAT || format_version == CDeterministicMN::MN_TYPE_FORMAT); size_t cnt = ReadCompactSize(s); for (size_t i = 0; i < cnt; i++) { if (evodb_migration) { @@ -383,8 +389,6 @@ public: [[nodiscard]] CDeterministicMNListDiff BuildDiff(const CDeterministicMNList& to) const; [[nodiscard]] CDeterministicMNList ApplyDiff(const CBlockIndex* pindex, const CDeterministicMNListDiff& diff) const; - void RepopulateUniquePropertyMap(); - void AddMN(const CDeterministicMNCPtr& dmn, bool fBumpTotalCount = true); void UpdateMN(const CDeterministicMN& oldDmn, const std::shared_ptr& pdmnState); void UpdateMN(const uint256& proTxHash, const std::shared_ptr& pdmnState); @@ -394,12 +398,12 @@ public: template [[nodiscard]] bool HasUniqueProperty(const T& v) const { - return mnUniquePropertyMap.count(::SerializeHash(v)) != 0; + return mnUniquePropertyMap.count(GetUniquePropertyHash(v)) != 0; } template [[nodiscard]] CDeterministicMNCPtr GetUniquePropertyMN(const T& v) const { - auto p = mnUniquePropertyMap.find(::SerializeHash(v)); + auto p = mnUniquePropertyMap.find(GetUniquePropertyHash(v)); if (!p) { return nullptr; } @@ -407,6 +411,14 @@ public: } private: + template + [[nodiscard]] uint256 GetUniquePropertyHash(const T& v) const + { + if constexpr (std::is_same()) { + assert(false); + } + return ::SerializeHash(v); + } template [[nodiscard]] bool AddUniqueProperty(const CDeterministicMN& dmn, const T& v) { @@ -415,7 +427,7 @@ private: return false; } - auto hash = ::SerializeHash(v); + auto hash = GetUniquePropertyHash(v); auto oldEntry = mnUniquePropertyMap.find(hash); if (oldEntry != nullptr && oldEntry->first != dmn.proTxHash) { return false; @@ -435,7 +447,7 @@ private: return false; } - auto oldHash = ::SerializeHash(oldValue); + auto oldHash = GetUniquePropertyHash(oldValue); auto p = mnUniquePropertyMap.find(oldHash); if (p == nullptr || p->first != dmn.proTxHash) { return false; @@ -464,6 +476,16 @@ private: } return true; } + + friend bool operator==(const CDeterministicMNList& a, const CDeterministicMNList& b) + { + return a.blockHash == b.blockHash && + a.nHeight == b.nHeight && + a.nTotalRegisteredCount == b.nTotalRegisteredCount && + a.mnMap == b.mnMap && + a.mnInternalIdMap == b.mnInternalIdMap && + a.mnUniquePropertyMap == b.mnUniquePropertyMap; + } }; class CDeterministicMNListDiff @@ -492,7 +514,7 @@ public: } template - void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_TYPE_FORMAT) + void Unserialize(Stream& s, const uint8_t format_version = CDeterministicMN::MN_CURRENT_FORMAT) { updatedMNs.clear(); removedMns.clear(); @@ -502,8 +524,6 @@ public: 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); @@ -595,6 +615,7 @@ public: bool IsDIP3Enforced(int nHeight = -1) LOCKS_EXCLUDED(cs); bool MigrateDBIfNeeded(); + bool MigrateDBIfNeeded2(); void DoMaintenance() LOCKS_EXCLUDED(cs); diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index 708649d29d..472cc3f615 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -28,7 +28,7 @@ std::string CDeterministicMNState::ToString() const return strprintf("CDeterministicMNState(nRegisteredHeight=%d, nLastPaidHeight=%d, nPoSePenalty=%d, nPoSeRevivedHeight=%d, nPoSeBanHeight=%d, nRevocationReason=%d, " "ownerAddress=%s, pubKeyOperator=%s, votingAddress=%s, addr=%s, payoutAddress=%s, operatorPayoutAddress=%s)", nRegisteredHeight, nLastPaidHeight, nPoSePenalty, nPoSeRevivedHeight, nPoSeBanHeight, nRevocationReason, - EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.Get().ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress); + EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), addr.ToStringIPPort(false), payoutAddress, operatorPayoutAddress); } void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const @@ -55,7 +55,7 @@ void CDeterministicMNState::ToJson(UniValue& obj, MnType nType) const if (ExtractDestination(scriptPayout, dest)) { obj.pushKV("payoutAddress", EncodeDestination(dest)); } - obj.pushKV("pubKeyOperator", pubKeyOperator.Get().ToString()); + obj.pushKV("pubKeyOperator", pubKeyOperator.ToString()); if (ExtractDestination(scriptOperatorPayout, dest)) { obj.pushKV("operatorPayoutAddress", EncodeDestination(dest)); } @@ -108,7 +108,7 @@ void CDeterministicMNStateDiff::ToJson(UniValue& obj, MnType nType) const } } if (fields & Field_pubKeyOperator) { - obj.pushKV("pubKeyOperator", state.pubKeyOperator.Get().ToString()); + obj.pushKV("pubKeyOperator", state.pubKeyOperator.ToString()); } if (nType == MnType::HighPerformance) { if (fields & Field_platformNodeID) { diff --git a/src/evo/dmnstate.h b/src/evo/dmnstate.h index d000171ff9..ab39fe96ef 100644 --- a/src/evo/dmnstate.h +++ b/src/evo/dmnstate.h @@ -72,6 +72,62 @@ public: } }; +// TODO: To remove this in the future +class CDeterministicMNState_mntype_format +{ +private: + int nPoSeBanHeight{-1}; + + friend class CDeterministicMNStateDiff; + friend 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}; + uint256 confirmedHash; + uint256 confirmedHashWithProRegTxHash; + CKeyID keyIDOwner; + CBLSLazyPublicKey pubKeyOperator; + CKeyID keyIDVoting; + CService addr; + CScript scriptPayout; + CScript scriptOperatorPayout; + + uint160 platformNodeID{}; + uint16_t platformP2PPort{0}; + uint16_t platformHTTPPort{0}; + +public: + CDeterministicMNState_mntype_format() = default; + + SERIALIZE_METHODS(CDeterministicMNState_mntype_format, 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); + } +}; + class CDeterministicMNState { private: @@ -80,6 +136,8 @@ private: friend class CDeterministicMNStateDiff; public: + int nVersion{CProRegTx::LEGACY_BLS_VERSION}; + int nRegisteredHeight{-1}; int nLastPaidHeight{0}; int nConsecutivePayments{0}; @@ -107,7 +165,9 @@ public: public: CDeterministicMNState() = default; explicit CDeterministicMNState(const CProRegTx& proTx) : + nVersion(proTx.nVersion), keyIDOwner(proTx.keyIDOwner), + pubKeyOperator(proTx.pubKeyOperator), keyIDVoting(proTx.keyIDVoting), addr(proTx.addr), scriptPayout(proTx.scriptPayout), @@ -115,7 +175,6 @@ public: platformP2PPort(proTx.platformP2PPort), platformHTTPPort(proTx.platformHTTPPort) { - pubKeyOperator.Set(proTx.pubKeyOperator); } explicit CDeterministicMNState(const CDeterministicMNState_Oldformat& s) : nPoSeBanHeight(s.nPoSeBanHeight), @@ -132,6 +191,27 @@ public: addr(s.addr), scriptPayout(s.scriptPayout), scriptOperatorPayout(s.scriptOperatorPayout) {} + + explicit CDeterministicMNState(const CDeterministicMNState_mntype_format& s) : + nPoSeBanHeight(s.nPoSeBanHeight), + nRegisteredHeight(s.nRegisteredHeight), + nLastPaidHeight(s.nLastPaidHeight), + nConsecutivePayments(s.nConsecutivePayments), + 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(s.platformNodeID), + platformP2PPort(s.platformP2PPort), + platformHTTPPort(s.platformHTTPPort) {} + template CDeterministicMNState(deserialize_type, Stream& s) { @@ -141,6 +221,7 @@ public: SERIALIZE_METHODS(CDeterministicMNState, obj) { READWRITE( + obj.nVersion, obj.nRegisteredHeight, obj.nLastPaidHeight, obj.nConsecutivePayments, @@ -151,7 +232,9 @@ public: obj.confirmedHash, obj.confirmedHashWithProRegTxHash, obj.keyIDOwner, - obj.pubKeyOperator, + obj.keyIDOwner); + READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), obj.nVersion == CProRegTx::LEGACY_BLS_VERSION)); + READWRITE( obj.keyIDVoting, obj.addr, obj.scriptPayout, @@ -163,7 +246,7 @@ public: void ResetOperatorFields() { - pubKeyOperator.Set(CBLSPublicKey()); + pubKeyOperator.Set(CBLSPublicKey(), bls::bls_legacy_scheme.load()); addr = CService(); scriptOperatorPayout = CScript(); nRevocationReason = CProUpRevTx::REASON_NOT_SPECIFIED; @@ -225,6 +308,7 @@ public: Field_platformNodeID = 0x8000, Field_platformP2PPort = 0x10000, Field_platformHTTPPort = 0x20000, + Field_nVersion = 0x40000, }; #define DMN_STATE_DIFF_ALL_FIELDS \ @@ -245,7 +329,8 @@ public: DMN_STATE_DIFF_LINE(nConsecutivePayments) \ DMN_STATE_DIFF_LINE(platformNodeID) \ DMN_STATE_DIFF_LINE(platformP2PPort) \ - DMN_STATE_DIFF_LINE(platformHTTPPort) + DMN_STATE_DIFF_LINE(platformHTTPPort) \ + DMN_STATE_DIFF_LINE(nVersion) public: uint32_t fields{0}; @@ -268,8 +353,7 @@ public: READWRITE(VARINT(obj.fields)); #define DMN_STATE_DIFF_LINE(f) \ if (strcmp(#f, "pubKeyOperator") == 0 && (obj.fields & Field_pubKeyOperator)) {\ - /* TODO: implement migration to Basic BLS after the fork */ \ - READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(obj.state.pubKeyOperator), true)); \ + READWRITE(CBLSLazyPublicKeyVersionWrapper(const_cast(obj.state.pubKeyOperator), obj.state.nVersion == CProRegTx::LEGACY_BLS_VERSION)); \ } else if (obj.fields & Field_##f) READWRITE(obj.state.f); DMN_STATE_DIFF_ALL_FIELDS diff --git a/src/evo/evodb.h b/src/evo/evodb.h index 9334603f24..9734711762 100644 --- a/src/evo/evodb.h +++ b/src/evo/evodb.h @@ -12,7 +12,8 @@ // "b_b" was used in the initial version of deterministic MN storage // "b_b2" was used after compact diffs were introduced // "b_b3" was used after masternode type introduction in evoDB -static const std::string EVODB_BEST_BLOCK = "b_b3"; +// "b_b4" was used after storing protx version for each masternode in evoDB +static const std::string EVODB_BEST_BLOCK = "b_b4"; class CEvoDB; diff --git a/src/evo/providertx.cpp b/src/evo/providertx.cpp index 8a17fef509..726ef8ed02 100644 --- a/src/evo/providertx.cpp +++ b/src/evo/providertx.cpp @@ -23,7 +23,7 @@ bool CProRegTx::IsTriviallyValid(bool is_bls_legacy_scheme, TxValidationState& s return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode"); } - if (keyIDOwner.IsNull() || !pubKeyOperator.IsValid() || keyIDVoting.IsNull()) { + if (keyIDOwner.IsNull() || !pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) { return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null"); } if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) { @@ -81,7 +81,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, ToUnderlying(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, ToUnderlying(nType), collateralOutpoint.ToStringShort(), addr.ToString(), (double)nOperatorReward / 100, EncodeDestination(PKHash(keyIDOwner)), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee, platformNodeID.ToString(), platformP2PPort, platformHTTPPort); } bool CProUpServTx::IsTriviallyValid(bool is_bls_legacy_scheme, TxValidationState& state) const @@ -114,7 +114,7 @@ bool CProUpRegTx::IsTriviallyValid(bool is_bls_legacy_scheme, TxValidationState& return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-protx-mode"); } - if (!pubKeyOperator.IsValid() || keyIDVoting.IsNull()) { + if (!pubKeyOperator.Get().IsValid() || keyIDVoting.IsNull()) { return state.Invalid(TxValidationResult::TX_BAD_SPECIAL, "bad-protx-key-null"); } if (!scriptPayout.IsPayToPublicKeyHash() && !scriptPayout.IsPayToScriptHash()) { @@ -132,7 +132,7 @@ std::string CProUpRegTx::ToString() const } return strprintf("CProUpRegTx(nVersion=%d, proTxHash=%s, pubKeyOperator=%s, votingAddress=%s, payoutAddress=%s)", - nVersion, proTxHash.ToString(), pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION), EncodeDestination(PKHash(keyIDVoting)), payee); + nVersion, proTxHash.ToString(), pubKeyOperator.ToString(), EncodeDestination(PKHash(keyIDVoting)), payee); } bool CProUpRevTx::IsTriviallyValid(bool is_bls_legacy_scheme, TxValidationState& state) const diff --git a/src/evo/providertx.h b/src/evo/providertx.h index 839dc8e9d1..85f7f921d9 100644 --- a/src/evo/providertx.h +++ b/src/evo/providertx.h @@ -43,7 +43,7 @@ public: uint16_t platformP2PPort{0}; uint16_t platformHTTPPort{0}; CKeyID keyIDOwner; - CBLSPublicKey pubKeyOperator; + CBLSLazyPublicKey pubKeyOperator; CKeyID keyIDVoting; uint16_t nOperatorReward{0}; CScript scriptPayout; @@ -65,7 +65,7 @@ public: obj.collateralOutpoint, obj.addr, obj.keyIDOwner, - CBLSPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)), + CBLSLazyPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)), obj.keyIDVoting, obj.nOperatorReward, obj.scriptPayout, @@ -104,7 +104,7 @@ public: if (ExtractDestination(scriptPayout, dest)) { obj.pushKV("payoutAddress", EncodeDestination(dest)); } - obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION)); + obj.pushKV("pubKeyOperator", pubKeyOperator.ToString()); obj.pushKV("operatorReward", (double)nOperatorReward / 100); if (nType == MnType::HighPerformance) { obj.pushKV("platformNodeID", platformNodeID.ToString()); @@ -212,7 +212,7 @@ public: uint16_t nVersion{LEGACY_BLS_VERSION}; // message version uint256 proTxHash; uint16_t nMode{0}; // only 0 supported for now - CBLSPublicKey pubKeyOperator; + CBLSLazyPublicKey pubKeyOperator; CKeyID keyIDVoting; CScript scriptPayout; uint256 inputsHash; // replay protection @@ -230,7 +230,7 @@ public: READWRITE( obj.proTxHash, obj.nMode, - CBLSPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)), + CBLSLazyPublicKeyVersionWrapper(const_cast(obj.pubKeyOperator), (obj.nVersion == LEGACY_BLS_VERSION)), obj.keyIDVoting, obj.scriptPayout, obj.inputsHash @@ -255,7 +255,7 @@ public: if (ExtractDestination(scriptPayout, dest)) { obj.pushKV("payoutAddress", EncodeDestination(dest)); } - obj.pushKV("pubKeyOperator", pubKeyOperator.ToString(nVersion == LEGACY_BLS_VERSION)); + obj.pushKV("pubKeyOperator", pubKeyOperator.ToString()); obj.pushKV("inputsHash", inputsHash.ToString()); } diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index c49981403f..b11e7ba7a2 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -167,6 +168,13 @@ bool ProcessSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, ll int64_t nTime6 = GetTimeMicros(); nTimeCbTxCL += nTime6 - nTime5; LogPrint(BCLog::BENCHMARK, " - CheckCbTxBestChainlock: %.2fms [%.2fs]\n", 0.001 * (nTime6 - nTime5), nTimeCbTxCL * 0.000001); + + if (llmq::utils::V19ActivationIndex(pindex) == pindex) { + // NOTE: The block next to the activation is the one that is using new rules. + // V19 activated just activated, so we must switch to the new rules here. + bls::bls_legacy_scheme.store(false); + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); + } } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); return state.Invalid(BlockValidationResult::BLOCK_CONSENSUS, "failed-procspectxsinblock"); @@ -179,7 +187,16 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq: { AssertLockHeld(cs_main); + auto bls_legacy_scheme = bls::bls_legacy_scheme.load(); + try { + if (llmq::utils::V19ActivationIndex(pindex) == pindex) { + // NOTE: The block next to the activation is the one that is using new rules. + // Removing the activation block here, so we must switch back to the old rules. + bls::bls_legacy_scheme.store(true); + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); + } + for (int i = (int)block.vtx.size() - 1; i >= 0; --i) { const CTransaction& tx = *block.vtx[i]; if (!UndoSpecialTx(tx, pindex)) { @@ -195,6 +212,8 @@ bool UndoSpecialTxsInBlock(const CBlock& block, const CBlockIndex* pindex, llmq: return false; } } catch (const std::exception& e) { + bls::bls_legacy_scheme.store(bls_legacy_scheme); + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); return error(strprintf("%s -- failed: %s\n", __func__, e.what()).c_str()); } diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 36861897a7..e5fb365848 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -500,7 +500,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf // Check that we have a valid MN signature if (!CheckSignature(dmn->pdmnState->pubKeyOperator.Get())) { - strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey = " + dmn->pdmnState->pubKeyOperator.Get().ToString(); + strError = "Invalid masternode signature for: " + strOutpoint + ", pubkey = " + dmn->pdmnState->pubKeyOperator.ToString(); return false; } diff --git a/src/init.cpp b/src/init.cpp index 7080a40789..b923cde9e9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1708,7 +1708,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc activeMasternodeInfo.blsKeyOperator = std::make_unique(keyOperator); activeMasternodeInfo.blsPubKeyOperator = std::make_unique(keyOperator.GetPublicKey()); } - LogPrintf("MASTERNODE:\n blsPubKeyOperator: %s\n", activeMasternodeInfo.blsPubKeyOperator->ToString()); + // We don't know the actual scheme at this point, print both + LogPrintf("MASTERNODE:\n blsPubKeyOperator legacy: %s\n blsPubKeyOperator basic: %s\n", + activeMasternodeInfo.blsPubKeyOperator->ToString(true), + activeMasternodeInfo.blsPubKeyOperator->ToString(false)); } else { LOCK(activeMasternodeInfoCs); activeMasternodeInfo.blsKeyOperator = std::make_unique(); @@ -2175,6 +2178,10 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc strLoadError = _("Error upgrading evo database"); break; } + if (!deterministicMNManager->MigrateDBIfNeeded2()) { + strLoadError = _("Error upgrading evo database"); + break; + } if (!llmq::quorumBlockProcessor->UpgradeDB()) { strLoadError = _("Error upgrading evo database"); @@ -2199,8 +2206,11 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc break; } - if (llmq::utils::IsV19Active(tip)) + bool v19active = llmq::utils::IsV19Active(tip); + if (llmq::utils::IsV19Active(tip)) { bls::bls_legacy_scheme.store(false); + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); + } if (!CVerifyDB().VerifyDB( *chainstate, chainparams, chainstate->CoinsDB(), @@ -2211,6 +2221,14 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc failed_verification = true; break; } + + // VerifyDB() disconnects blocks which might result in us switching back to legacy. + // Make sure we use the right scheme. + if (v19active && bls::bls_legacy_scheme.load()) { + bls::bls_legacy_scheme.store(false); + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); + } + } else { // TODO: CEvoDB instance should probably be a part of CChainState // (for multiple chainstates to actually work in parallel) diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index bfb1e721e5..4e54d62a97 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -148,9 +148,6 @@ bool CQuorumBlockProcessor::ProcessBlock(const CBlock& block, const CBlockIndex* return true; } - if (utils::IsV19Active(pindex->pprev)) - bls::bls_legacy_scheme.store(false); - llmq::utils::PreComputeQuorumMembers(pindex); std::multimap qcs; @@ -307,9 +304,6 @@ bool CQuorumBlockProcessor::UndoBlock(const CBlock& block, const CBlockIndex* pi { AssertLockHeld(cs_main); - if (!utils::IsV19Active(pindex->pprev)) - bls::bls_legacy_scheme.store(true); - llmq::utils::PreComputeQuorumMembers(pindex, true); std::multimap qcs; diff --git a/src/llmq/signing.h b/src/llmq/signing.h index 3706334cf6..041b18157c 100644 --- a/src/llmq/signing.h +++ b/src/llmq/signing.h @@ -76,7 +76,7 @@ public: CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSLazySignature& _sig) : CSigBase(_llmqType, _quorumHash, _id, _msgHash), sig(_sig) {UpdateHash();}; CRecoveredSig(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash, const CBLSSignature& _sig) : - CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast(sig).Set(_sig); UpdateHash();}; + CSigBase(_llmqType, _quorumHash, _id, _msgHash) {const_cast(sig).Set(_sig, bls::bls_legacy_scheme.load()); UpdateHash();}; private: // only in-memory diff --git a/src/llmq/signing_shares.cpp b/src/llmq/signing_shares.cpp index 08bef971a0..116f7591d2 100644 --- a/src/llmq/signing_shares.cpp +++ b/src/llmq/signing_shares.cpp @@ -1513,7 +1513,7 @@ std::optional CSigSharesManager::CreateSigShare(const CQuorumCPtr& qu CSigShare sigShare(quorum->params.type, quorum->qc->quorumHash, id, msgHash, uint16_t(memberIdx), {}); uint256 signHash = sigShare.buildSignHash(); - sigShare.sigShare.Set(skShare.Sign(signHash)); + sigShare.sigShare.Set(skShare.Sign(signHash), bls::bls_legacy_scheme.load()); if (!sigShare.sigShare.Get().IsValid()) { LogPrintf("CSigSharesManager::%s -- failed to sign sigShare. signHash=%s, id=%s, msgHash=%s, time=%s\n", __func__, signHash.ToString(), sigShare.getId().ToString(), sigShare.getMsgHash().ToString(), t.count()); diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index 1a503bfa65..b05cf1e5de 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -131,6 +131,7 @@ void CActiveMasternodeManager::Init(const CBlockIndex* pindex) activeMasternodeInfo.proTxHash = dmn->proTxHash; activeMasternodeInfo.outpoint = dmn->collateralOutpoint; + activeMasternodeInfo.legacy = dmn->pdmnState->nVersion == CProRegTx::LEGACY_BLS_VERSION; state = MASTERNODE_READY; } diff --git a/src/masternode/node.h b/src/masternode/node.h index 6fbe57578f..2d05caa343 100644 --- a/src/masternode/node.h +++ b/src/masternode/node.h @@ -28,6 +28,7 @@ struct CActiveMasternodeInfo { uint256 proTxHash; COutPoint outpoint; CService service; + bool legacy{true}; }; diff --git a/src/rpc/evo.cpp b/src/rpc/evo.cpp index 3e48b69021..3935a1b9d6 100644 --- a/src/rpc/evo.cpp +++ b/src/rpc/evo.cpp @@ -660,7 +660,7 @@ static UniValue protx_register_common_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", specific_legacy_bls_scheme && !isHPMNrequested); + ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[paramIdx + 2].get_str(), "operator BLS address", ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION); CKeyID keyIDVoting = ptx.keyIDOwner; if (request.params[paramIdx + 3].get_str() != "") { @@ -702,7 +702,6 @@ static UniValue protx_register_common_wrapper(const JSONRPCRequest& request, paramIdx += 3; } - ptx.pubKeyOperator = pubKeyOperator; ptx.keyIDVoting = keyIDVoting; ptx.scriptPayout = GetScriptForDestination(payoutDest); @@ -1040,12 +1039,12 @@ static UniValue protx_update_registrar_wrapper(const JSONRPCRequest& request, co if (!dmn) { throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("masternode %s not found", ptx.proTxHash.ToString())); } - ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator.Get(); + ptx.pubKeyOperator = dmn->pdmnState->pubKeyOperator; ptx.keyIDVoting = dmn->pdmnState->keyIDVoting; ptx.scriptPayout = dmn->pdmnState->scriptPayout; if (request.params[1].get_str() != "") { - ptx.pubKeyOperator = ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", specific_legacy_bls_scheme); + ptx.pubKeyOperator.Set(ParseBLSPubKey(request.params[1].get_str(), "operator BLS address", ptx.nVersion == CProUpRegTx::LEGACY_BLS_VERSION), ptx.nVersion == CProRegTx::LEGACY_BLS_VERSION); } if (request.params[2].get_str() != "") { ptx.keyIDVoting = ParsePubKeyIDFromAddress(request.params[2].get_str(), "voting address"); diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 6adf475423..3096bd0d32 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -314,7 +314,7 @@ static UniValue gobject_submit(const JSONRPCRequest& request) bool fMnFound = WITH_LOCK(activeMasternodeInfoCs, return mnList.HasValidMNByCollateral(activeMasternodeInfo.outpoint)); LogPrint(BCLog::GOBJECT, "gobject_submit -- pubKeyOperator = %s, outpoint = %s, params.size() = %lld, fMnFound = %d\n", - (WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsPubKeyOperator ? activeMasternodeInfo.blsPubKeyOperator->ToString() : "N/A")), + (WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.blsPubKeyOperator ? activeMasternodeInfo.blsPubKeyOperator->ToString(activeMasternodeInfo.legacy) : "N/A")), WITH_LOCK(activeMasternodeInfoCs, return activeMasternodeInfo.outpoint.ToStringShort()), request.params.size(), fMnFound); // ASSEMBLE NEW GOVERNANCE OBJECT FROM USER PARAMETERS diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 13a1eb3292..63ebb8a2d7 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -675,7 +675,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner)) << " " << EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting)) << " " << collateralAddressStr << " " << - dmn.pdmnState->pubKeyOperator.Get().ToString(); + dmn.pdmnState->pubKeyOperator.ToString(); std::string strInfo = streamInfo.str(); if (strFilter !="" && strInfo.find(strFilter) == std::string::npos && strOutpoint.find(strFilter) == std::string::npos) return; @@ -697,7 +697,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) objMN.pushKV("owneraddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner))); objMN.pushKV("votingaddress", EncodeDestination(PKHash(dmn.pdmnState->keyIDVoting))); objMN.pushKV("collateraladdress", collateralAddressStr); - objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.Get().ToString()); + objMN.pushKV("pubkeyoperator", dmn.pdmnState->pubKeyOperator.ToString()); obj.pushKV(strOutpoint, objMN); } else if (strMode == "lastpaidblock") { if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return; @@ -714,7 +714,7 @@ static UniValue masternodelist(const JSONRPCRequest& request) obj.pushKV(strOutpoint, EncodeDestination(PKHash(dmn.pdmnState->keyIDOwner))); } else if (strMode == "pubkeyoperator") { if (strFilter !="" && strOutpoint.find(strFilter) == std::string::npos) return; - obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.Get().ToString()); + obj.pushKV(strOutpoint, dmn.pdmnState->pubKeyOperator.ToString()); } else if (strMode == "status") { std::string strStatus = dmnToStatus(dmn); if (strFilter !="" && strStatus.find(strFilter) == std::string::npos && diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index c4ea9ed769..1a4d8675f0 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -211,7 +211,7 @@ static UniValue BuildQuorumInfo(const llmq::CQuorumCPtr& quorum, bool includeMem UniValue mo(UniValue::VOBJ); mo.pushKV("proTxHash", dmn->proTxHash.ToString()); mo.pushKV("service", dmn->pdmnState->addr.ToString()); - mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.Get().ToString()); + mo.pushKV("pubKeyOperator", dmn->pdmnState->pubKeyOperator.ToString()); mo.pushKV("valid", quorum->qc->validMembers[i]); if (quorum->qc->validMembers[i]) { CBLSPublicKey pubKey = quorum->GetPubKeyShare(i); diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp index b6dca0085d..c0a751352e 100644 --- a/src/test/block_reward_reallocation_tests.cpp +++ b/src/test/block_reward_reallocation_tests.cpp @@ -117,7 +117,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM proTx.collateralOutpoint.n = 0; proTx.addr = LookupNumeric("1.1.1.1", port); proTx.keyIDOwner = ownerKeyRet.GetPubKey().GetID(); - proTx.pubKeyOperator = operatorKeyRet.GetPublicKey(); + proTx.pubKeyOperator.Set(operatorKeyRet.GetPublicKey(), bls::bls_legacy_scheme.load()); proTx.keyIDVoting = ownerKeyRet.GetPubKey().GetID(); proTx.scriptPayout = scriptPayout; diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index 95f7f11550..b48aed3f02 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -103,7 +104,7 @@ static CMutableTransaction CreateProRegTx(const CTxMemPool& mempool, SimpleUTXOM proTx.collateralOutpoint.n = 0; proTx.addr = LookupNumeric("1.1.1.1", port); proTx.keyIDOwner = ownerKeyRet.GetPubKey().GetID(); - proTx.pubKeyOperator = operatorKeyRet.GetPublicKey(); + proTx.pubKeyOperator.Set(operatorKeyRet.GetPublicKey(), bls::bls_legacy_scheme.load()); proTx.keyIDVoting = ownerKeyRet.GetPubKey().GetID(); proTx.scriptPayout = scriptPayout; @@ -143,7 +144,7 @@ static CMutableTransaction CreateProUpRegTx(const CTxMemPool& mempool, SimpleUTX CProUpRegTx proTx; proTx.nVersion = CProUpRegTx::GetVersion(!bls::bls_legacy_scheme); proTx.proTxHash = proTxHash; - proTx.pubKeyOperator = pubKeyOperator; + proTx.pubKeyOperator.Set(pubKeyOperator, bls::bls_legacy_scheme.load()); proTx.keyIDVoting = keyIDVoting; proTx.scriptPayout = scriptPayout; @@ -264,6 +265,136 @@ void FuncDIP3Activation(TestChainSetup& setup) BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); }; +void FuncV19Activation(TestChainSetup& setup) +{ + BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip())); + + // create + auto utxos = BuildSimpleUtxoMap(setup.m_coinbase_txns); + CKey owner_key; + CBLSSecretKey operator_key; + CKey collateral_key; + collateral_key.MakeNewKey(false); + auto collateralScript = GetScriptForDestination(PKHash(collateral_key.GetPubKey())); + auto tx_reg = CreateProRegTx(*(setup.m_node.mempool), utxos, 1, collateralScript, setup.coinbaseKey, owner_key, operator_key); + auto tx_reg_hash = tx_reg.GetHash(); + + int nHeight = ::ChainActive().Height(); + + auto block = std::make_shared(setup.CreateBlock({tx_reg}, setup.coinbaseKey)); + BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr)); + BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip())); + ++nHeight; + BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight); + deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); + deterministicMNManager->DoMaintenance(); + auto tip_list = deterministicMNManager->GetListAtChainTip(); + BOOST_ASSERT(tip_list.HasMN(tx_reg_hash)); + auto pindex_create = ::ChainActive().Tip(); + auto base_list = deterministicMNManager->GetListForBlock(pindex_create); + std::vector diffs; + + // update + CBLSSecretKey operator_key_new; + operator_key_new.MakeNewKey(); + auto tx_upreg = CreateProUpRegTx(*(setup.m_node.mempool), utxos, tx_reg_hash, owner_key, operator_key_new.GetPublicKey(), owner_key.GetPubKey().GetID(), collateralScript, setup.coinbaseKey); + + block = std::make_shared(setup.CreateBlock({tx_upreg}, setup.coinbaseKey)); + BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr)); + BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip())); + ++nHeight; + BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight); + deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); + deterministicMNManager->DoMaintenance(); + tip_list = deterministicMNManager->GetListAtChainTip(); + BOOST_ASSERT(tip_list.HasMN(tx_reg_hash)); + diffs.push_back(base_list.BuildDiff(tip_list)); + + // spend + CMutableTransaction tx_spend; + COutPoint collateralOutpoint(tx_reg_hash, 0); + tx_spend.vin.emplace_back(collateralOutpoint); + tx_spend.vout.emplace_back(999.99 * COIN, collateralScript); + + FillableSigningProvider signing_provider; + signing_provider.AddKeyPubKey(collateral_key, collateral_key.GetPubKey()); + BOOST_ASSERT(SignSignature(signing_provider, CTransaction(tx_reg), tx_spend, 0, SIGHASH_ALL)); + block = std::make_shared(setup.CreateBlock({tx_spend}, setup.coinbaseKey)); + BOOST_ASSERT(Assert(setup.m_node.chainman)->ProcessNewBlock(Params(), block, true, nullptr)); + BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip())); + ++nHeight; + BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight); + deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); + deterministicMNManager->DoMaintenance(); + diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip())); + tip_list = deterministicMNManager->GetListAtChainTip(); + BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash)); + BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash)); + + // mine another block so that it's not the last one before V19 + setup.CreateAndProcessBlock({}, setup.coinbaseKey); + BOOST_ASSERT(!llmq::utils::IsV19Active(::ChainActive().Tip())); + ++nHeight; + BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight); + deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); + deterministicMNManager->DoMaintenance(); + diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip())); + tip_list = deterministicMNManager->GetListAtChainTip(); + BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash)); + BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash)); + + // this block should activate V19 + setup.CreateAndProcessBlock({}, setup.coinbaseKey); + BOOST_ASSERT(llmq::utils::IsV19Active(::ChainActive().Tip())); + ++nHeight; + BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight); + deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); + deterministicMNManager->DoMaintenance(); + diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip())); + tip_list = deterministicMNManager->GetListAtChainTip(); + BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash)); + BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash)); + + // check mn list/diff + CDeterministicMNListDiff dummy_diff = base_list.BuildDiff(tip_list); + CDeterministicMNList dummmy_list = base_list.ApplyDiff(::ChainActive().Tip(), dummy_diff); + // Lists should match + BOOST_ASSERT(dummmy_list == tip_list); + + // mine 10 more blocks + for (int i = 0; i < 10; ++i) + { + setup.CreateAndProcessBlock({}, setup.coinbaseKey); + BOOST_ASSERT(llmq::utils::IsV19Active(::ChainActive().Tip())); + BOOST_CHECK_EQUAL(::ChainActive().Height(), nHeight + 1 + i); + deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); + deterministicMNManager->DoMaintenance(); + diffs.push_back(tip_list.BuildDiff(deterministicMNManager->GetListAtChainTip())); + tip_list = deterministicMNManager->GetListAtChainTip(); + BOOST_ASSERT(!tip_list.HasMN(tx_reg_hash)); + BOOST_ASSERT(deterministicMNManager->GetListForBlock(pindex_create).HasMN(tx_reg_hash)); + } + + // check mn list/diff + const CBlockIndex* v19_index = llmq::utils::V19ActivationIndex(::ChainActive().Tip()); + auto v19_list = deterministicMNManager->GetListForBlock(v19_index); + dummy_diff = v19_list.BuildDiff(tip_list); + dummmy_list = v19_list.ApplyDiff(::ChainActive().Tip(), dummy_diff); + BOOST_ASSERT(dummmy_list == tip_list); + + // NOTE: this fails on v19/v19.1 with errors like: + // "RemoveMN: Can't delete a masternode ... with a pubKeyOperator=..." + dummy_diff = base_list.BuildDiff(tip_list); + dummmy_list = base_list.ApplyDiff(::ChainActive().Tip(), dummy_diff); + BOOST_ASSERT(dummmy_list == tip_list); + + dummmy_list = base_list; + for (const auto& diff : diffs) { + dummmy_list = dummmy_list.ApplyDiff(::ChainActive().Tip(), diff); + } + BOOST_ASSERT(dummmy_list == tip_list); +}; + void FuncDIP3Protx(TestChainSetup& setup) { CKey sporkKey; @@ -481,7 +612,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) payload.nVersion = CProRegTx::GetVersion(!bls::bls_legacy_scheme); payload.addr = LookupNumeric("1.1.1.1", 1); payload.keyIDOwner = ownerKey.GetPubKey().GetID(); - payload.pubKeyOperator = operatorKey.GetPublicKey(); + payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load()); payload.keyIDVoting = ownerKey.GetPubKey().GetID(); payload.scriptPayout = scriptPayout; @@ -550,7 +681,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) CProRegTx payload; payload.addr = LookupNumeric("1.1.1.1", 2); payload.keyIDOwner = ownerKey.GetPubKey().GetID(); - payload.pubKeyOperator = operatorKey.GetPublicKey(); + payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load()); payload.keyIDVoting = ownerKey.GetPubKey().GetID(); payload.scriptPayout = scriptPayout; @@ -612,7 +743,7 @@ void FuncVerifyDB(TestChainSetup& setup) payload.nVersion = CProRegTx::GetVersion(!bls::bls_legacy_scheme); payload.addr = LookupNumeric("1.1.1.1", 1); payload.keyIDOwner = ownerKey.GetPubKey().GetID(); - payload.pubKeyOperator = operatorKey.GetPublicKey(); + payload.pubKeyOperator.Set(operatorKey.GetPublicKey(), bls::bls_legacy_scheme.load()); payload.keyIDVoting = ownerKey.GetPubKey().GetID(); payload.scriptPayout = scriptPayout; @@ -667,6 +798,13 @@ BOOST_AUTO_TEST_CASE(dip3_activation_legacy) FuncDIP3Activation(setup); } +// V19 can only be activated with legacy scheme +BOOST_AUTO_TEST_CASE(v19_activation_legacy) +{ + TestChainV19BeforeActivationSetup setup; + FuncV19Activation(setup); +} + BOOST_AUTO_TEST_CASE(dip3_protx_legacy) { TestChainDIP3Setup setup; diff --git a/src/test/evo_simplifiedmns_tests.cpp b/src/test/evo_simplifiedmns_tests.cpp index 9129f8e8db..3f632d2824 100644 --- a/src/test/evo_simplifiedmns_tests.cpp +++ b/src/test/evo_simplifiedmns_tests.cpp @@ -28,7 +28,7 @@ BOOST_AUTO_TEST_CASE(simplifiedmns_merkleroots) std::vector vecBytes{static_cast(i)}; vecBytes.resize(CBLSSecretKey::SerSize); - smle.pubKeyOperator.Set(CBLSSecretKey(vecBytes).GetPublicKey()); + smle.pubKeyOperator.Set(CBLSSecretKey(vecBytes).GetPublicKey(), bls::bls_legacy_scheme.load()); smle.keyIDVoting.SetHex(strprintf("%040x", i)); smle.isValid = true; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 8d9a33932e..8c82bffc30 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -392,3 +393,9 @@ CBlock getBlock13b8a() stream >> block; return block; } + +TestChainV19BeforeActivationSetup::TestChainV19BeforeActivationSetup() : TestChainSetup(894) +{ + bool v19_active = llmq::utils::IsV19Active(::ChainActive().Tip()); + assert(!v19_active); +} diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index bd9a41a533..17c417e040 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -154,6 +154,11 @@ struct TestChainDIP3BeforeActivationSetup : public TestChainSetup TestChainDIP3BeforeActivationSetup() : TestChainSetup(430) {} }; +struct TestChainV19BeforeActivationSetup : public TestChainSetup +{ + TestChainV19BeforeActivationSetup(); +}; + class CTxMemPoolEntry; struct TestMemPoolEntryHelper diff --git a/src/txmempool.cpp b/src/txmempool.cpp index cf106b6b2a..1f09267774 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -19,7 +19,6 @@ #include #include -#include #include #include #include @@ -438,7 +437,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash); assert(dmn); newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator); - if (dmn->pdmnState->pubKeyOperator.Get() != proTx.pubKeyOperator) { + if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) { newit->isKeyChangeProTx = true; } } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { @@ -835,7 +834,7 @@ void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID } } -void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey) +void CTxMemPool::removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) { if (mapProTxBlsPubKeyHashes.count(pubKey.GetHash())) { uint256 conflictHash = mapProTxBlsPubKeyHashes[pubKey.GetHash()]; @@ -1300,7 +1299,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { return true; // i.e. failed to find validated ProTx == conflict } // only allow one operator key change in the mempool - if (dmn->pdmnState->pubKeyOperator.Get() != proTx.pubKeyOperator) { + if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) { if (hasKeyChangeInMempool(proTx.proTxHash)) { return true; } diff --git a/src/txmempool.h b/src/txmempool.h index 76cf3c7cb9..718246f7c7 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -33,8 +34,6 @@ #include #include -class CBLSPublicKey; - class CBlockIndex; class CChainState; extern RecursiveMutex cs_main; @@ -625,7 +624,7 @@ public: void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs); - void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs); + void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSLazyPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxSpentCollateralConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxKeyChangedConflicts(const CTransaction &tx, const uint256& proTxHash, const uint256& newKeyHash) EXCLUSIVE_LOCKS_REQUIRED(cs); diff --git a/src/validation.cpp b/src/validation.cpp index 1979e09f94..f3cc735413 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4345,6 +4345,8 @@ bool TestBlockValidity(BlockValidationState& state, assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip()); + auto bls_legacy_scheme = bls::bls_legacy_scheme.load(); + uint256 hash = block.GetHash(); if (clhandler.HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) { LogPrintf("ERROR: %s: conflicting with chainlock\n", __func__); @@ -4373,6 +4375,12 @@ bool TestBlockValidity(BlockValidationState& state, return false; assert(state.IsValid()); + // we could switch to another scheme while testing, switch back to the original one + if (bls_legacy_scheme != bls::bls_legacy_scheme.load()) { + bls::bls_legacy_scheme.store(bls_legacy_scheme); + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls::bls_legacy_scheme.load()); + } + return true; } diff --git a/test/functional/feature_dip3_v19.py b/test/functional/feature_dip3_v19.py index 855bb76451..497dc82fc3 100755 --- a/test/functional/feature_dip3_v19.py +++ b/test/functional/feature_dip3_v19.py @@ -66,9 +66,18 @@ class DIP3V19Test(DashTestFramework): b_0 = self.nodes[0].getbestblockhash() self.test_getmnlistdiff(null_hash, b_0, {}, [], expected_updated) + mn_list_before = self.nodes[0].masternodelist() + pubkeyoperator_list_before = set([mn_list_before[e]["pubkeyoperator"] for e in mn_list_before]) + self.activate_v19(expected_activation_height=900) self.log.info("Activated v19 at height:" + str(self.nodes[0].getblockcount())) + mn_list_after = self.nodes[0].masternodelist() + pubkeyoperator_list_after = set([mn_list_after[e]["pubkeyoperator"] for e in mn_list_after]) + + self.log.info("pubkeyoperator should still be shown using legacy scheme") + assert_equal(pubkeyoperator_list_before, pubkeyoperator_list_after) + self.move_to_next_cycle() self.log.info("Cycle H height:" + str(self.nodes[0].getblockcount())) self.move_to_next_cycle()