mirror of
https://github.com/dashpay/dash.git
synced 2024-12-24 19:42:46 +01:00
evo: Fix two mempool issues (#4154)
* evo: Remove all protx-es that refer to a ProRegTx removed from mempool * tests: Check that removal of ProRegTx causes removal of other protx-es that refer to it * evo: Consider tx itself a collateral in mempool maps when payload collateral hash is null * tests: Should not allow a ProRegTx which uses another ProRegTx as an external collateral to enter mempool Signed-off-by: pasta <pasta@dashboost.org>
This commit is contained in:
parent
027c4653ba
commit
9f9a08ae80
@ -14,6 +14,7 @@
|
||||
#include <policy/policy.h>
|
||||
#include <keystore.h>
|
||||
#include <spork.h>
|
||||
#include <txmempool.h>
|
||||
|
||||
#include <evo/specialtx.h>
|
||||
#include <evo/providertx.h>
|
||||
@ -438,6 +439,138 @@ BOOST_FIXTURE_TEST_CASE(dip3_protx, TestChainDIP3Setup)
|
||||
const_cast<Consensus::Params&>(Params().GetConsensus()).DIP0003EnforcementHeight = DIP0003EnforcementHeightBackup;
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(dip3_test_mempool_reorg, TestChainDIP3Setup)
|
||||
{
|
||||
int nHeight = chainActive.Height();
|
||||
auto utxos = BuildSimpleUtxoMap(coinbaseTxns);
|
||||
|
||||
CKey ownerKey;
|
||||
CKey payoutKey;
|
||||
CKey collateralKey;
|
||||
CBLSSecretKey operatorKey;
|
||||
|
||||
ownerKey.MakeNewKey(true);
|
||||
payoutKey.MakeNewKey(true);
|
||||
collateralKey.MakeNewKey(true);
|
||||
operatorKey.MakeNewKey();
|
||||
|
||||
auto scriptPayout = GetScriptForDestination(payoutKey.GetPubKey().GetID());
|
||||
auto scriptCollateral = GetScriptForDestination(collateralKey.GetPubKey().GetID());
|
||||
|
||||
// Create a MN with an external collateral
|
||||
CMutableTransaction tx_collateral;
|
||||
FundTransaction(tx_collateral, utxos, scriptCollateral, 1000 * COIN, coinbaseKey);
|
||||
SignTransaction(tx_collateral, coinbaseKey);
|
||||
|
||||
auto block = std::make_shared<CBlock>(CreateBlock({tx_collateral}, coinbaseKey));
|
||||
BOOST_ASSERT(ProcessNewBlock(Params(), block, true, nullptr));
|
||||
deterministicMNManager->UpdatedBlockTip(chainActive.Tip());
|
||||
BOOST_ASSERT(chainActive.Height() == nHeight + 1);
|
||||
BOOST_ASSERT(block->GetHash() == chainActive.Tip()->GetBlockHash());
|
||||
|
||||
CProRegTx payload;
|
||||
payload.addr = LookupNumeric("1.1.1.1", 1);
|
||||
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
|
||||
payload.pubKeyOperator = operatorKey.GetPublicKey();
|
||||
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
|
||||
payload.scriptPayout = scriptPayout;
|
||||
|
||||
for (size_t i = 0; i < tx_collateral.vout.size(); ++i) {
|
||||
if (tx_collateral.vout[i].nValue == 1000 * COIN) {
|
||||
payload.collateralOutpoint = COutPoint(tx_collateral.GetHash(), i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CMutableTransaction tx_reg;
|
||||
tx_reg.nVersion = 3;
|
||||
tx_reg.nType = TRANSACTION_PROVIDER_REGISTER;
|
||||
FundTransaction(tx_reg, utxos, scriptPayout, 1000 * COIN, coinbaseKey);
|
||||
payload.inputsHash = CalcTxInputsHash(tx_reg);
|
||||
CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey);
|
||||
SetTxPayload(tx_reg, payload);
|
||||
SignTransaction(tx_reg, coinbaseKey);
|
||||
|
||||
CTxMemPool testPool;
|
||||
TestMemPoolEntryHelper entry;
|
||||
LOCK(testPool.cs);
|
||||
|
||||
// Create ProUpServ and test block reorg which double-spend ProRegTx
|
||||
auto tx_up_serv = CreateProUpServTx(utxos, tx_reg.GetHash(), operatorKey, 2, CScript(), coinbaseKey);
|
||||
testPool.addUnchecked(tx_up_serv.GetHash(), entry.FromTx(tx_up_serv));
|
||||
// A disconnected block would insert ProRegTx back into mempool
|
||||
testPool.addUnchecked(tx_reg.GetHash(), entry.FromTx(tx_reg));
|
||||
BOOST_CHECK_EQUAL(testPool.size(), 2U);
|
||||
|
||||
// Create a tx that will double-spend ProRegTx
|
||||
CMutableTransaction tx_reg_ds;
|
||||
tx_reg_ds.vin = tx_reg.vin;
|
||||
tx_reg_ds.vout.emplace_back(0, CScript() << OP_RETURN);
|
||||
SignTransaction(tx_reg_ds, coinbaseKey);
|
||||
|
||||
// Check mempool as if a new block with tx_reg_ds was connected instead of the old one with tx_reg
|
||||
std::vector<CTransactionRef> block_reorg;
|
||||
block_reorg.emplace_back(std::make_shared<CTransaction>(tx_reg_ds));
|
||||
testPool.removeForBlock(block_reorg, nHeight + 2);
|
||||
BOOST_CHECK_EQUAL(testPool.size(), 0U);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(dip3_test_mempool_dual_proregtx, TestChainDIP3Setup)
|
||||
{
|
||||
int nHeight = chainActive.Height();
|
||||
auto utxos = BuildSimpleUtxoMap(coinbaseTxns);
|
||||
|
||||
// Create a MN
|
||||
CKey ownerKey1;
|
||||
CBLSSecretKey operatorKey1;
|
||||
auto tx_reg1 = CreateProRegTx(utxos, 1, GenerateRandomAddress(), coinbaseKey, ownerKey1, operatorKey1);
|
||||
|
||||
// Create a MN with an external collateral that references tx_reg1
|
||||
CKey ownerKey;
|
||||
CKey payoutKey;
|
||||
CKey collateralKey;
|
||||
CBLSSecretKey operatorKey;
|
||||
|
||||
ownerKey.MakeNewKey(true);
|
||||
payoutKey.MakeNewKey(true);
|
||||
collateralKey.MakeNewKey(true);
|
||||
operatorKey.MakeNewKey();
|
||||
|
||||
auto scriptPayout = GetScriptForDestination(payoutKey.GetPubKey().GetID());
|
||||
auto scriptCollateral = GetScriptForDestination(collateralKey.GetPubKey().GetID());
|
||||
|
||||
CProRegTx payload;
|
||||
payload.addr = LookupNumeric("1.1.1.1", 2);
|
||||
payload.keyIDOwner = ownerKey.GetPubKey().GetID();
|
||||
payload.pubKeyOperator = operatorKey.GetPublicKey();
|
||||
payload.keyIDVoting = ownerKey.GetPubKey().GetID();
|
||||
payload.scriptPayout = scriptPayout;
|
||||
|
||||
for (size_t i = 0; i < tx_reg1.vout.size(); ++i) {
|
||||
if (tx_reg1.vout[i].nValue == 1000 * COIN) {
|
||||
payload.collateralOutpoint = COutPoint(tx_reg1.GetHash(), i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CMutableTransaction tx_reg2;
|
||||
tx_reg2.nVersion = 3;
|
||||
tx_reg2.nType = TRANSACTION_PROVIDER_REGISTER;
|
||||
FundTransaction(tx_reg2, utxos, scriptPayout, 1000 * COIN, coinbaseKey);
|
||||
payload.inputsHash = CalcTxInputsHash(tx_reg2);
|
||||
CMessageSigner::SignMessage(payload.MakeSignString(), payload.vchSig, collateralKey);
|
||||
SetTxPayload(tx_reg2, payload);
|
||||
SignTransaction(tx_reg2, coinbaseKey);
|
||||
|
||||
CTxMemPool testPool;
|
||||
TestMemPoolEntryHelper entry;
|
||||
LOCK(testPool.cs);
|
||||
|
||||
testPool.addUnchecked(tx_reg1.GetHash(), entry.FromTx(tx_reg1));
|
||||
BOOST_CHECK_EQUAL(testPool.size(), 1U);
|
||||
BOOST_CHECK(testPool.existsProviderTxConflict(tx_reg2));
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(dip3_verify_db, TestChainDIP3Setup)
|
||||
{
|
||||
int nHeight = chainActive.Height();
|
||||
|
@ -426,6 +426,8 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||
mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), tx.GetHash());
|
||||
if (!proTx.collateralOutpoint.hash.IsNull()) {
|
||||
mapProTxCollaterals.emplace(proTx.collateralOutpoint, tx.GetHash());
|
||||
} else {
|
||||
mapProTxCollaterals.emplace(COutPoint(tx.GetHash(), proTx.collateralOutpoint.n), tx.GetHash());
|
||||
}
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
CProUpServTx proTx;
|
||||
@ -656,6 +658,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
|
||||
mapProTxPubKeyIDs.erase(proTx.keyIDOwner);
|
||||
mapProTxBlsPubKeyHashes.erase(proTx.pubKeyOperator.GetHash());
|
||||
mapProTxCollaterals.erase(proTx.collateralOutpoint);
|
||||
mapProTxCollaterals.erase(COutPoint(it->GetTx().GetHash(), proTx.collateralOutpoint.n));
|
||||
} else if (it->GetTx().nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
CProUpServTx proTx;
|
||||
if (!GetTxPayload(it->GetTx(), proTx)) {
|
||||
@ -797,6 +800,23 @@ void CTxMemPool::removeConflicts(const CTransaction &tx)
|
||||
const CTransaction &txConflict = *it->second;
|
||||
if (txConflict != tx)
|
||||
{
|
||||
if (txConflict.nType == TRANSACTION_PROVIDER_REGISTER) {
|
||||
// Remove all other protxes which refer to this protx
|
||||
// NOTE: Can't use equal_range here as every call to removeRecursive might invalidate iterators
|
||||
while (true) {
|
||||
auto itPro = mapProTxRefs.find(txConflict.GetHash());
|
||||
if (itPro == mapProTxRefs.end()) {
|
||||
break;
|
||||
}
|
||||
auto txit = mapTx.find(itPro->second);
|
||||
if (txit != mapTx.end()) {
|
||||
ClearPrioritisation(txit->GetTx().GetHash());
|
||||
removeRecursive(txit->GetTx(), MemPoolRemovalReason::CONFLICT);
|
||||
} else {
|
||||
mapProTxRefs.erase(itPro);
|
||||
}
|
||||
}
|
||||
}
|
||||
ClearPrioritisation(txConflict.GetHash());
|
||||
removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT);
|
||||
}
|
||||
@ -909,6 +929,8 @@ void CTxMemPool::removeProTxConflicts(const CTransaction &tx)
|
||||
removeProTxPubKeyConflicts(tx, proTx.pubKeyOperator);
|
||||
if (!proTx.collateralOutpoint.hash.IsNull()) {
|
||||
removeProTxCollateralConflicts(tx, proTx.collateralOutpoint);
|
||||
} else {
|
||||
removeProTxCollateralConflicts(tx, COutPoint(tx.GetHash(), proTx.collateralOutpoint.n));
|
||||
}
|
||||
} else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_SERVICE) {
|
||||
CProUpServTx proTx;
|
||||
|
Loading…
Reference in New Issue
Block a user