diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 56437de93..15acafde6 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -472,6 +472,13 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, } mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash()); mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), tx.GetHash()); + + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash); + assert(dmn); // we should never get such a ProTx into the mempool + newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator); + if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) { + newit->isKeyChangeProTx = true; + } } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { CProUpRevTx proTx; if (!GetTxPayload(tx, proTx)) { @@ -479,6 +486,12 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, return false; } mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash()); + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash); + assert(dmn); // we should never get such a ProTx into the mempool + newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator); + if (dmn->pdmnState->pubKeyOperator != CBLSPublicKey()) { + newit->isKeyChangeProTx = true; + } } return true; @@ -893,6 +906,24 @@ void CTxMemPool::removeProTxSpentCollateralConflicts(const CTransaction &tx) } } +void CTxMemPool::removeProTxKeyChangedConflicts(const CTransaction &tx, const uint256& proTxHash, const uint256& newKeyHash) +{ + std::set conflictingTxs; + for (auto its = mapProTxRefs.equal_range(proTxHash); its.first != its.second; ++its.first) { + auto txit = mapTx.find(its.first->second); + if (txit == mapTx.end()) { + continue; + } + if (txit->validForProTxKey != newKeyHash) { + conflictingTxs.emplace(txit->GetTx().GetHash()); + } + } + for (const auto& txHash : conflictingTxs) { + auto& tx = mapTx.find(txHash)->GetTx(); + removeRecursive(tx, MemPoolRemovalReason::CONFLICT); + } +} + void CTxMemPool::removeProTxConflicts(const CTransaction &tx) { removeProTxSpentCollateralConflicts(tx); @@ -936,6 +967,15 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx) } removeProTxPubKeyConflicts(tx, proTx.pubKeyOperator); + removeProTxKeyChangedConflicts(tx, proTx.proTxHash, ::SerializeHash(proTx.pubKeyOperator)); + } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { + CProUpRevTx proTx; + if (!GetTxPayload(tx, proTx)) { + LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); + return; + } + + removeProTxKeyChangedConflicts(tx, proTx.proTxHash, ::SerializeHash(CBLSPublicKey())); } } @@ -1218,6 +1258,20 @@ TxMempoolInfo CTxMemPool::info(const uint256& hash) const bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { LOCK(cs); + + auto hasKeyChangeInMempool = [&](const uint256& proTxHash) { + for (auto its = mapProTxRefs.equal_range(proTxHash); its.first != its.second; ++its.first) { + auto txit = mapTx.find(its.first->second); + if (txit == mapTx.end()) { + continue; + } + if (txit->isKeyChangeProTx) { + return true; + } + } + return false; + }; + if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { CProRegTx proTx; if (!GetTxPayload(tx, proTx)) { @@ -1243,8 +1297,33 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return true; // i.e. can't decode payload == conflict } + + // only allow one operator key change in the mempool + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash); + assert(dmn); // this method should only be called with validated ProTxs + if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) { + if (hasKeyChangeInMempool(proTx.proTxHash)) { + return true; + } + } + auto it = mapProTxBlsPubKeyHashes.find(proTx.pubKeyOperator.GetHash()); return it != mapProTxBlsPubKeyHashes.end() && it->second != proTx.proTxHash; + } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { + CProUpRevTx proTx; + if (!GetTxPayload(tx, proTx)) { + LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); + return true; // i.e. can't decode payload == conflict + } + + // only allow one operator key change in the mempool + auto dmn = deterministicMNManager->GetListAtChainTip().GetMN(proTx.proTxHash); + assert(dmn); // this method should only be called with validated ProTxs + if (dmn->pdmnState->pubKeyOperator != CBLSPublicKey()) { + if (hasKeyChangeInMempool(proTx.proTxHash)) { + return true; + } + } } return false; } diff --git a/src/txmempool.h b/src/txmempool.h index 1362c8064..e88edf80f 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -162,6 +162,10 @@ public: unsigned int GetSigOpCountWithAncestors() const { return nSigOpCountWithAncestors; } mutable size_t vTxHashesIdx; //!< Index in mempool's vTxHashes + + // If this is a proTx, this will be the hash of the key for which this ProTx was valid + mutable uint256 validForProTxKey; + mutable bool isKeyChangeProTx{false}; }; // Helpers for modifying CTxMemPool::mapTx, which is a boost multi_index. @@ -586,6 +590,7 @@ public: void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey); void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint); void removeProTxSpentCollateralConflicts(const CTransaction &tx); + void removeProTxKeyChangedConflicts(const CTransaction &tx, const uint256& proTxHash, const uint256& newKeyHash); void removeProTxConflicts(const CTransaction &tx); void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight);