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
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);
}

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());
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)) {

View File

@ -534,6 +534,7 @@ private:
typedef std::map<uint256, std::vector<CSpentIndexKey> > mapSpentIndexInserted;
mapSpentIndexInserted mapSpentInserted;
std::multimap<uint256, uint256> mapProTxRefs; // proTxHash -> transaction (all TXs that refer to an existing proTx)
std::map<CService, uint256> mapProTxAddresses;
std::map<CKeyID, uint256> mapProTxPubKeyIDs;
std::map<uint256, uint256> 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<CTransactionRef>& vtx, unsigned int nBlockHeight);