Skip to content

Commit 64f6836

Browse files
fix: conflict ProReg collateral reuse with mempool MN updates
existsProviderTxConflict now links a replacement ProRegTx that reuses a live external collateral to any in-mempool ProUpServ/ProUpReg/ProUpRev for the MN being replaced, so both cannot coexist and CreateNewBlock cannot package the bad-protx-hash ordering. removeProTxConflicts also drops those updates when such a replacement is mined. Consensus block acceptance is unchanged; only mempool packaging/eviction is tightened.
1 parent fee2296 commit 64f6836

1 file changed

Lines changed: 40 additions & 0 deletions

File tree

‎src/txmempool.cpp‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,24 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
10501050
}
10511051
if (!proTx.collateralOutpoint.hash.IsNull()) {
10521052
removeProTxCollateralConflicts(tx, proTx.collateralOutpoint);
1053+
// Replacement of a live MN via external-collateral reuse: drop any
1054+
// mempool updates that still target the proTxHash being replaced.
1055+
// removeProTxSpentCollateralConflicts only covers spends, not reuse.
1056+
if (auto dmn = m_dmnman.load(std::memory_order_acquire)->GetListAtChainTip().GetMNByCollateral(proTx.collateralOutpoint)) {
1057+
// Can't use equal_range + erase loop: removeRecursive may invalidate iterators.
1058+
while (true) {
1059+
auto it = mapProTxRefs.find(dmn->proTxHash);
1060+
if (it == mapProTxRefs.end()) {
1061+
break;
1062+
}
1063+
auto conflictIt = mapTx.find(it->second);
1064+
if (conflictIt != mapTx.end()) {
1065+
removeRecursive(conflictIt->GetTx(), MemPoolRemovalReason::CONFLICT);
1066+
} else {
1067+
mapProTxRefs.erase(it);
1068+
}
1069+
}
1070+
}
10531071
} else {
10541072
removeProTxCollateralConflicts(tx, COutPoint(tx_hash, proTx.collateralOutpoint.n));
10551073
}
@@ -1422,6 +1440,14 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
14221440
// there is another tx that spends the collateral
14231441
return true;
14241442
}
1443+
// A replacement ProRegTx deletes the live MN backed by this collateral. Any
1444+
// in-mempool update that still targets that MN's proTxHash would then fail
1445+
// BuildNewListFromBlock with bad-protx-hash if both were mined in one block.
1446+
if (auto dmn = dmnman->GetListAtChainTip().GetMNByCollateral(proTx.collateralOutpoint)) {
1447+
if (mapProTxRefs.find(dmn->proTxHash) != mapProTxRefs.end()) {
1448+
return true;
1449+
}
1450+
}
14251451
}
14261452
return false;
14271453
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
@@ -1442,6 +1468,12 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
14421468
return true;
14431469
}
14441470
}
1471+
// Conflict with a replacement ProRegTx that reuses this MN's external collateral.
1472+
if (auto dmn = dmnman->GetListAtChainTip().GetMN(opt_proTx->proTxHash)) {
1473+
if (mapProTxCollaterals.count(dmn->collateralOutpoint)) {
1474+
return true;
1475+
}
1476+
}
14451477
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REGISTRAR) {
14461478
const auto opt_proTx = GetTxPayload<CProUpRegTx>(tx);
14471479
if (!opt_proTx) {
@@ -1456,6 +1488,10 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
14561488
LogPrint(BCLog::MEMPOOL, "%s: ERROR: Masternode is not in the list, proTxHash: %s\n", __func__, proTx.proTxHash.ToString());
14571489
return true; // i.e. failed to find validated ProTx == conflict
14581490
}
1491+
// Conflict with a replacement ProRegTx that reuses this MN's external collateral.
1492+
if (mapProTxCollaterals.count(dmn->collateralOutpoint)) {
1493+
return true;
1494+
}
14591495
// only allow one operator key change in the mempool
14601496
if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) {
14611497
if (hasKeyChangeInMempool(proTx.proTxHash)) {
@@ -1478,6 +1514,10 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const {
14781514
LogPrint(BCLog::MEMPOOL, "%s: ERROR: Masternode is not in the list, proTxHash: %s\n", __func__, proTx.proTxHash.ToString());
14791515
return true; // i.e. failed to find validated ProTx == conflict
14801516
}
1517+
// Conflict with a replacement ProRegTx that reuses this MN's external collateral.
1518+
if (mapProTxCollaterals.count(dmn->collateralOutpoint)) {
1519+
return true;
1520+
}
14811521
// only allow one operator key change in the mempool
14821522
if (dmn->pdmnState->pubKeyOperator.Get() != CBLSPublicKey()) {
14831523
if (hasKeyChangeInMempool(proTx.proTxHash)) {

0 commit comments

Comments
 (0)