diff --git a/src/test/test_dash.cpp b/src/test/test_dash.cpp index 7bc3f7e9f7..521d8a422f 100644 --- a/src/test/test_dash.cpp +++ b/src/test/test_dash.cpp @@ -50,11 +50,16 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName) fPrintToDebugLog = false; // don't want to write to debug.log file fCheckBlockIndex = true; SelectParams(chainName); + evoDb = new CEvoDB(1 << 20, true, true); + deterministicMNManager = new CDeterministicMNManager(*evoDb); noui_connect(); } BasicTestingSetup::~BasicTestingSetup() { + delete deterministicMNManager; + delete evoDb; + ECC_Stop(); g_connman.reset(); } @@ -70,10 +75,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha boost::filesystem::create_directories(pathTemp); ForceSetArg("-datadir", pathTemp.string()); mempool.setSanityCheck(1.0); - evoDb = new CEvoDB(1 << 20, true, true); pblocktree = new CBlockTreeDB(1 << 20, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true); - deterministicMNManager = new CDeterministicMNManager(*evoDb); llmq::InitLLMQSystem(*evoDb); pcoinsTip = new CCoinsViewCache(pcoinsdbview); InitBlockIndex(chainparams); @@ -98,10 +101,8 @@ TestingSetup::~TestingSetup() UnloadBlockIndex(); delete pcoinsTip; llmq::DestroyLLMQSystem(); - delete deterministicMNManager; delete pcoinsdbview; delete pblocktree; - delete evoDb; boost::filesystem::remove_all(pathTemp); } diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 46b4c4b74c..56437de93a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -447,6 +447,9 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return false; } + if (!proTx.collateralOutpoint.hash.IsNull()) { + mapProTxRefs.emplace(tx.GetHash(), proTx.collateralOutpoint.hash); + } mapProTxAddresses.emplace(proTx.addr, tx.GetHash()); mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, tx.GetHash()); mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), tx.GetHash()); @@ -459,6 +462,7 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return false; } + mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash()); mapProTxAddresses.emplace(proTx.addr, tx.GetHash()); } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { CProUpRegTx proTx; @@ -466,7 +470,15 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); return false; } + mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash()); mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), tx.GetHash()); + } 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 false; + } + mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash()); } return true; @@ -644,11 +656,25 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) } else vTxHashes.clear(); + auto eraseProTxRef = [&](const uint256& proTxHash, const uint256& txHash) { + auto its = mapProTxRefs.equal_range(proTxHash); + for (auto it = its.first; it != its.second;) { + if (it->second == txHash) { + it = mapProTxRefs.erase(it); + } else { + ++it; + } + } + }; + if (it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) { CProRegTx proTx; if (!GetTxPayload(it->GetTx(), proTx)) { assert(false); } + if (!proTx.collateralOutpoint.IsNull()) { + eraseProTxRef(it->GetTx().GetHash(), proTx.collateralOutpoint.hash); + } mapProTxAddresses.erase(proTx.addr); mapProTxPubKeyIDs.erase(proTx.keyIDOwner); mapProTxBlsPubKeyHashes.erase(proTx.pubKeyOperator.GetHash()); @@ -658,13 +684,21 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) if (!GetTxPayload(it->GetTx(), proTx)) { assert(false); } + eraseProTxRef(proTx.proTxHash, it->GetTx().GetHash()); mapProTxAddresses.erase(proTx.addr); } else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { CProUpRegTx proTx; if (!GetTxPayload(it->GetTx(), proTx)) { assert(false); } + eraseProTxRef(proTx.proTxHash, it->GetTx().GetHash()); mapProTxBlsPubKeyHashes.erase(proTx.pubKeyOperator.GetHash()); + } else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { + CProUpRevTx proTx; + if (!GetTxPayload(it->GetTx(), proTx)) { + assert(false); + } + eraseProTxRef(proTx.proTxHash, it->GetTx().GetHash()); } totalTxSize -= it->GetTxSize(); @@ -823,8 +857,46 @@ void CTxMemPool::removeProTxCollateralConflicts(const CTransaction &tx, const CO } } +void CTxMemPool::removeProTxSpentCollateralConflicts(const CTransaction &tx) +{ + // Remove TXs that refer to a MN for which the collateral was spent + auto removeSpentCollateralConflict = [&](const uint256& proTxHash) { + // Can't use equal_range here as every call to removeRecursive might invalidate iterators + while (true) { + auto it = mapProTxRefs.find(proTxHash); + if (it == mapProTxRefs.end()) { + break; + } + auto conflictIt = mapTx.find(it->second); + if (conflictIt != mapTx.end()) { + removeRecursive(conflictIt->GetTx(), MemPoolRemovalReason::CONFLICT); + } else { + // Should not happen as we track referencing TXs in addUnchecked/removeUnchecked. + // But lets be on the safe side and not run into an endless loop... + LogPrintf("%s: ERROR: found invalid TX ref in mapProTxRefs, proTxHash=%s, txHash=%s", __func__, proTxHash.ToString(), it->second.ToString()); + mapProTxRefs.erase(it); + } + } + }; + auto mnList = deterministicMNManager->GetListAtChainTip(); + for (const auto& in : tx.vin) { + auto collateralIt = mapProTxCollaterals.find(in.prevout); + if (collateralIt != mapProTxCollaterals.end()) { + // These are not yet mined ProRegTxs + removeSpentCollateralConflict(collateralIt->second); + } + auto dmn = mnList.GetMNByCollateral(in.prevout); + if (dmn) { + // These are updates refering to a mined ProRegTx + removeSpentCollateralConflict(dmn->proTxHash); + } + } +} + void CTxMemPool::removeProTxConflicts(const CTransaction &tx) { + removeProTxSpentCollateralConflicts(tx); + if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { CProRegTx proTx; if (!GetTxPayload(tx, proTx)) { diff --git a/src/txmempool.h b/src/txmempool.h index 81ceedc53d..1362c80643 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -534,6 +534,7 @@ private: typedef std::map > mapSpentIndexInserted; mapSpentIndexInserted mapSpentInserted; + std::multimap mapProTxRefs; // proTxHash -> transaction (all TXs that refer to an existing proTx) std::map mapProTxAddresses; std::map mapProTxPubKeyIDs; std::map mapProTxBlsPubKeyHashes; @@ -584,6 +585,7 @@ public: void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId); void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey); void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint); + void removeProTxSpentCollateralConflicts(const CTransaction &tx); void removeProTxConflicts(const CTransaction &tx); void removeForBlock(const std::vector& vtx, unsigned int nBlockHeight);