Add withdraw-claimed tracking to mempool and enforce it

This commit is contained in:
Matt Corallo 2016-03-18 22:39:41 -07:00 committed by Gregory Sanders
parent 16063366d9
commit 79d422acfa
4 changed files with 56 additions and 5 deletions

View file

@ -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)
{

View file

@ -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)

View file

@ -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.

View file

@ -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