mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-14 12:43:40 +02:00
Add withdraw-claimed tracking to mempool and enforce it
This commit is contained in:
parent
16063366d9
commit
79d422acfa
4 changed files with 56 additions and 5 deletions
18
src/main.cpp
18
src/main.cpp
|
|
@ -1228,6 +1228,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||
{
|
||||
CCoinsView dummy;
|
||||
CCoinsViewCache view(&dummy);
|
||||
std::set<std::pair<uint256, COutPoint> > setWithdrawsSpent;
|
||||
|
||||
CAmount nValueIn = 0;
|
||||
LockPoints lp;
|
||||
|
|
@ -1261,6 +1262,18 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||
if (!view.HaveInputs(tx))
|
||||
return state.Invalid(false, REJECT_DUPLICATE, "bad-txns-inputs-spent");
|
||||
|
||||
BOOST_FOREACH(const CTxIn &txin, tx.vin)
|
||||
{
|
||||
CCoins coins;
|
||||
assert(view.GetCoins(txin.prevout.hash, coins));
|
||||
if (coins.vout[txin.prevout.n].scriptPubKey.IsWithdrawLock() && txin.scriptSig.IsWithdrawProof()) {
|
||||
pair<uint256, COutPoint> outpoint = make_pair(coins.vout[txin.prevout.n].scriptPubKey.GetWithdrawLockGenesisHash(), txin.scriptSig.GetWithdrawSpent());
|
||||
if (view.IsWithdrawSpent(outpoint))
|
||||
return state.Invalid(false, REJECT_CONFLICT, "withdraw-already-claimed");
|
||||
setWithdrawsSpent.insert(outpoint);
|
||||
}
|
||||
}
|
||||
|
||||
// Bring the best block into scope
|
||||
view.GetBestBlock();
|
||||
|
||||
|
|
@ -1309,7 +1322,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||
}
|
||||
}
|
||||
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp);
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOpsCost, lp, setWithdrawsSpent);
|
||||
unsigned int nSize = entry.GetTxSize();
|
||||
|
||||
// Check that the transaction doesn't have an excessive number of
|
||||
|
|
@ -1533,6 +1546,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||
return false;
|
||||
}
|
||||
|
||||
assert(setWithdrawsSpent2 == setWithdrawsSpent);
|
||||
setWithdrawsSpent2.clear();
|
||||
|
||||
// Check again against just the consensus-critical mandatory script
|
||||
|
|
@ -1550,6 +1564,8 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
|||
__func__, hash.ToString(), FormatStateMessage(state));
|
||||
}
|
||||
|
||||
assert(setWithdrawsSpent2 == setWithdrawsSpent);
|
||||
|
||||
// Remove conflicting transactions from the mempool
|
||||
BOOST_FOREACH(const CTxMemPool::txiter it, allConflicting)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -140,8 +140,9 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CTransaction &txn, CTxMemPool *po
|
|||
// Hack to assume either its completely dependent on other mempool txs or not at all
|
||||
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
|
||||
|
||||
std::set<std::pair<uint256, COutPoint> > mapWithdrawsSpent;
|
||||
return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
|
||||
hasNoDependencies, inChainValue, spendsCoinbase, sigOpCost, lp);
|
||||
hasNoDependencies, inChainValue, spendsCoinbase, sigOpCost, lp, mapWithdrawsSpent);
|
||||
}
|
||||
|
||||
void Shutdown(void* parg)
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ using namespace std;
|
|||
CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
||||
bool poolHasNoInputsOf, CAmount _inChainInputValue,
|
||||
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp):
|
||||
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp, std::set<std::pair<uint256, COutPoint> >& _setWithdrawsSpent):
|
||||
tx(std::make_shared<CTransaction>(_tx)), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
|
||||
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
|
||||
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
|
||||
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp), setWithdrawsSpent(_setWithdrawsSpent)
|
||||
{
|
||||
nTxWeight = GetTransactionWeight(_tx);
|
||||
nModSize = _tx.CalculateModifiedSize(GetTxSize());
|
||||
|
|
@ -447,6 +447,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
|||
vTxHashes.emplace_back(tx.GetWitnessHash(), newit);
|
||||
newit->vTxHashesIdx = vTxHashes.size() - 1;
|
||||
|
||||
typedef std::pair<uint256, COutPoint> WithdrawPair;
|
||||
BOOST_FOREACH(const WithdrawPair& it, entry.setWithdrawsSpent)
|
||||
assert(mapWithdrawsSpentToTxid.insert(std::make_pair(it, hash)).second);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -456,6 +460,10 @@ void CTxMemPool::removeUnchecked(txiter it)
|
|||
BOOST_FOREACH(const CTxIn& txin, it->GetTx().vin)
|
||||
mapNextTx.erase(txin.prevout);
|
||||
|
||||
typedef std::pair<uint256, COutPoint> WithdrawPair;
|
||||
BOOST_FOREACH(const WithdrawPair& it2, it->setWithdrawsSpent)
|
||||
assert(mapWithdrawsSpentToTxid.erase(it2));
|
||||
|
||||
if (vTxHashes.size() > 1) {
|
||||
vTxHashes[it->vTxHashesIdx] = std::move(vTxHashes.back());
|
||||
vTxHashes[it->vTxHashesIdx].second->vTxHashesIdx = it->vTxHashesIdx;
|
||||
|
|
@ -660,6 +668,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
|
||||
LOCK(cs);
|
||||
list<const CTxMemPoolEntry*> waitingOnDependants;
|
||||
set<pair<uint256, COutPoint> > setGlobalWithdrawsSpent;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
unsigned int i = 0;
|
||||
checkTotal += it->GetTxSize();
|
||||
|
|
@ -741,6 +750,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
std::set<std::pair<uint256, COutPoint> > setWithdrawsSpent;
|
||||
assert(CheckInputs(tx, state, mempoolDuplicate, false, 0, false, txdata, setWithdrawsSpent, NULL));
|
||||
UpdateCoins(tx, mempoolDuplicate, 1000000);
|
||||
assert(setWithdrawsSpent == it->setWithdrawsSpent);
|
||||
size_t prevWithdrawsCount = setGlobalWithdrawsSpent.size();
|
||||
setGlobalWithdrawsSpent.insert(setWithdrawsSpent.begin(), setWithdrawsSpent.end());
|
||||
assert(setGlobalWithdrawsSpent.size() == prevWithdrawsCount + setWithdrawsSpent.size());
|
||||
}
|
||||
}
|
||||
unsigned int stepsSinceLastRemove = 0;
|
||||
|
|
@ -757,6 +770,10 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
std::set<std::pair<uint256, COutPoint> > setWithdrawsSpent;
|
||||
assert(CheckInputs(entry->GetTx(), state, mempoolDuplicate, false, 0, false, txdata, setWithdrawsSpent, NULL));
|
||||
UpdateCoins(entry->GetTx(), mempoolDuplicate, 1000000);
|
||||
assert(setWithdrawsSpent == entry->setWithdrawsSpent);
|
||||
size_t prevWithdrawsCount = setGlobalWithdrawsSpent.size();
|
||||
setGlobalWithdrawsSpent.insert(setWithdrawsSpent.begin(), setWithdrawsSpent.end());
|
||||
assert(setGlobalWithdrawsSpent.size() == prevWithdrawsCount + setWithdrawsSpent.size());
|
||||
stepsSinceLastRemove = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -768,6 +785,14 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
|||
assert(&tx == it->second);
|
||||
}
|
||||
|
||||
for (std::set<std::pair<uint256, COutPoint> >::const_iterator it = setGlobalWithdrawsSpent.begin(); it != setGlobalWithdrawsSpent.end(); it++) {
|
||||
assert(!pcoins->IsWithdrawSpent(*it));
|
||||
}
|
||||
for (std::map<std::pair<uint256, COutPoint>, uint256>::const_iterator it = mapWithdrawsSpentToTxid.begin(); it != mapWithdrawsSpentToTxid.end(); it++) {
|
||||
assert(setGlobalWithdrawsSpent.erase(it->first));
|
||||
}
|
||||
assert(setGlobalWithdrawsSpent.size() == 0);
|
||||
|
||||
assert(totalTxSize == checkTotal);
|
||||
assert(innerUsage == cachedInnerUsage);
|
||||
}
|
||||
|
|
@ -984,6 +1009,10 @@ bool CCoinsViewMemPool::HaveCoins(const uint256 &txid) const {
|
|||
return mempool.exists(txid) || base->HaveCoins(txid);
|
||||
}
|
||||
|
||||
bool CCoinsViewMemPool::IsWithdrawSpent(const std::pair<uint256, COutPoint> &outpoint) const {
|
||||
return mempool.mapWithdrawsSpentToTxid.count(outpoint) || base->IsWithdrawSpent(outpoint);
|
||||
}
|
||||
|
||||
size_t CTxMemPool::DynamicMemoryUsage() const {
|
||||
LOCK(cs);
|
||||
// Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented.
|
||||
|
|
|
|||
|
|
@ -107,10 +107,12 @@ private:
|
|||
int64_t nSigOpCostWithAncestors;
|
||||
|
||||
public:
|
||||
std::set<std::pair<uint256, COutPoint> > setWithdrawsSpent;
|
||||
|
||||
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
||||
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase,
|
||||
int64_t nSigOpsCost, LockPoints lp);
|
||||
int64_t nSigOpsCost, LockPoints lp, std::set<std::pair<uint256, COutPoint> >& setWithdrawsSpent);
|
||||
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
||||
|
||||
const CTransaction& GetTx() const { return *this->tx; }
|
||||
|
|
@ -473,6 +475,8 @@ public:
|
|||
|
||||
const setEntries & GetMemPoolParents(txiter entry) const;
|
||||
const setEntries & GetMemPoolChildren(txiter entry) const;
|
||||
|
||||
std::map<std::pair<uint256, COutPoint>, uint256> mapWithdrawsSpentToTxid;
|
||||
private:
|
||||
typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap;
|
||||
|
||||
|
|
@ -693,6 +697,7 @@ public:
|
|||
CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn);
|
||||
bool GetCoins(const uint256 &txid, CCoins &coins) const;
|
||||
bool HaveCoins(const uint256 &txid) const;
|
||||
bool IsWithdrawSpent(const std::pair<uint256, COutPoint> &outpoint) const;
|
||||
};
|
||||
|
||||
// We want to sort transactions by coin age priority
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue