Remove ProTxs from mempool that refer to a ProRegTx for which the collateral was spent (#2539)

* Track TXs in mempool which refer to an existing ProTx

* When a block is added, remove TXs that refer to ProTxs with spent collaterals

* Initalize evoDb and deterministicMNManager in BasicTestingSetup

instead of TestingSetup. Now that the mempool uses deterministicMNManager,
we need to initialize it for all tests that might use the mempool.
This commit is contained in:
Alexander Block 2018-12-10 06:03:57 +01:00 committed by GitHub
parent cbd0303529
commit 511dc3714c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 4 deletions

View File

@ -50,11 +50,16 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName)
fPrintToDebugLog = false; // don't want to write to debug.log file fPrintToDebugLog = false; // don't want to write to debug.log file
fCheckBlockIndex = true; fCheckBlockIndex = true;
SelectParams(chainName); SelectParams(chainName);
evoDb = new CEvoDB(1 << 20, true, true);
deterministicMNManager = new CDeterministicMNManager(*evoDb);
noui_connect(); noui_connect();
} }
BasicTestingSetup::~BasicTestingSetup() BasicTestingSetup::~BasicTestingSetup()
{ {
delete deterministicMNManager;
delete evoDb;
ECC_Stop(); ECC_Stop();
g_connman.reset(); g_connman.reset();
} }
@ -70,10 +75,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
boost::filesystem::create_directories(pathTemp); boost::filesystem::create_directories(pathTemp);
ForceSetArg("-datadir", pathTemp.string()); ForceSetArg("-datadir", pathTemp.string());
mempool.setSanityCheck(1.0); mempool.setSanityCheck(1.0);
evoDb = new CEvoDB(1 << 20, true, true);
pblocktree = new CBlockTreeDB(1 << 20, true); pblocktree = new CBlockTreeDB(1 << 20, true);
pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsdbview = new CCoinsViewDB(1 << 23, true);
deterministicMNManager = new CDeterministicMNManager(*evoDb);
llmq::InitLLMQSystem(*evoDb); llmq::InitLLMQSystem(*evoDb);
pcoinsTip = new CCoinsViewCache(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinsdbview);
InitBlockIndex(chainparams); InitBlockIndex(chainparams);
@ -98,10 +101,8 @@ TestingSetup::~TestingSetup()
UnloadBlockIndex(); UnloadBlockIndex();
delete pcoinsTip; delete pcoinsTip;
llmq::DestroyLLMQSystem(); llmq::DestroyLLMQSystem();
delete deterministicMNManager;
delete pcoinsdbview; delete pcoinsdbview;
delete pblocktree; delete pblocktree;
delete evoDb;
boost::filesystem::remove_all(pathTemp); boost::filesystem::remove_all(pathTemp);
} }

View File

@ -447,6 +447,9 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString()); LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false; return false;
} }
if (!proTx.collateralOutpoint.hash.IsNull()) {
mapProTxRefs.emplace(tx.GetHash(), proTx.collateralOutpoint.hash);
}
mapProTxAddresses.emplace(proTx.addr, tx.GetHash()); mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, tx.GetHash()); mapProTxPubKeyIDs.emplace(proTx.keyIDOwner, tx.GetHash());
mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), 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()); LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false; return false;
} }
mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash());
mapProTxAddresses.emplace(proTx.addr, tx.GetHash()); mapProTxAddresses.emplace(proTx.addr, tx.GetHash());
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx; 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()); LogPrintf("%s: ERROR: Invalid transaction payload, tx: %s", __func__, tx.ToString());
return false; return false;
} }
mapProTxRefs.emplace(proTx.proTxHash, tx.GetHash());
mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), 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; return true;
@ -644,11 +656,25 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
} else } else
vTxHashes.clear(); 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) { if (it->GetTx().nType == TRANSACTION_PROVIDER_REGISTER) {
CProRegTx proTx; CProRegTx proTx;
if (!GetTxPayload(it->GetTx(), proTx)) { if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false); assert(false);
} }
if (!proTx.collateralOutpoint.IsNull()) {
eraseProTxRef(it->GetTx().GetHash(), proTx.collateralOutpoint.hash);
}
mapProTxAddresses.erase(proTx.addr); mapProTxAddresses.erase(proTx.addr);
mapProTxPubKeyIDs.erase(proTx.keyIDOwner); mapProTxPubKeyIDs.erase(proTx.keyIDOwner);
mapProTxBlsPubKeyHashes.erase(proTx.pubKeyOperator.GetHash()); mapProTxBlsPubKeyHashes.erase(proTx.pubKeyOperator.GetHash());
@ -658,13 +684,21 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
if (!GetTxPayload(it->GetTx(), proTx)) { if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false); assert(false);
} }
eraseProTxRef(proTx.proTxHash, it->GetTx().GetHash());
mapProTxAddresses.erase(proTx.addr); mapProTxAddresses.erase(proTx.addr);
} else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) { } else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
CProUpRegTx proTx; CProUpRegTx proTx;
if (!GetTxPayload(it->GetTx(), proTx)) { if (!GetTxPayload(it->GetTx(), proTx)) {
assert(false); assert(false);
} }
eraseProTxRef(proTx.proTxHash, it->GetTx().GetHash());
mapProTxBlsPubKeyHashes.erase(proTx.pubKeyOperator.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(); 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) void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
{ {
removeProTxSpentCollateralConflicts(tx);
if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { if (tx.nType == TRANSACTION_PROVIDER_REGISTER) {
CProRegTx proTx; CProRegTx proTx;
if (!GetTxPayload(tx, proTx)) { if (!GetTxPayload(tx, proTx)) {

View File

@ -534,6 +534,7 @@ private:
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted; typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
mapSpentIndexInserted mapSpentInserted; mapSpentIndexInserted mapSpentInserted;
std::multimap<uint256, uint256> mapProTxRefs; // proTxHash -> transaction (all TXs that refer to an existing proTx)
std::map<CService, uint256> mapProTxAddresses; std::map<CService, uint256> mapProTxAddresses;
std::map<CKeyID, uint256> mapProTxPubKeyIDs; std::map<CKeyID, uint256> mapProTxPubKeyIDs;
std::map<uint256, uint256> mapProTxBlsPubKeyHashes; std::map<uint256, uint256> mapProTxBlsPubKeyHashes;
@ -584,6 +585,7 @@ public:
void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId); void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId);
void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey); void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey);
void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint); void removeProTxCollateralConflicts(const CTransaction &tx, const COutPoint &collateralOutpoint);
void removeProTxSpentCollateralConflicts(const CTransaction &tx);
void removeProTxConflicts(const CTransaction &tx); void removeProTxConflicts(const CTransaction &tx);
void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight);