Track operator key changes in mempool and handle conflicts (#2540)

* Track ProTx operator key changes in mempool

* Remove ProTx conflicts from mempool when ProUpRegTx or ProUpRevTx changed keys

* Only allow one operator key change per MN in in mempool
This commit is contained in:
Alexander Block 2018-12-10 09:14:19 +01:00 committed by GitHub
parent 88f7bf0d82
commit 5830353373
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 84 additions and 0 deletions

View File

@ -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<uint256> 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;
}

View File

@ -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<CTransactionRef>& vtx, unsigned int nBlockHeight);