mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
Add LockPoints
Obtain LockPoints to store in CTxMemPoolEntry and during a reorg, evaluate whether they are still valid and if not, recalculate them.
This commit is contained in:
parent
8b70a64d62
commit
982670c333
89
src/main.cpp
89
src/main.cpp
@ -794,7 +794,25 @@ bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeig
|
||||
return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block));
|
||||
}
|
||||
|
||||
bool CheckSequenceLocks(const CTransaction &tx, int flags)
|
||||
bool TestLockPointValidity(const LockPoints* lp)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
assert(lp);
|
||||
// If there are relative lock times then the maxInputBlock will be set
|
||||
// If there are no relative lock times, the LockPoints don't depend on the chain
|
||||
if (lp->maxInputBlock) {
|
||||
// Check whether chainActive is an extension of the block at which the LockPoints
|
||||
// calculation was valid. If not LockPoints are no longer valid
|
||||
if (!chainActive.Contains(lp->maxInputBlock)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// LockPoints still valid
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool useExistingLockPoints)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(mempool.cs);
|
||||
@ -810,25 +828,57 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags)
|
||||
// *next* block, we need to use one more than chainActive.Height()
|
||||
index.nHeight = tip->nHeight + 1;
|
||||
|
||||
// pcoinsTip contains the UTXO set for chainActive.Tip()
|
||||
CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
|
||||
std::vector<int> prevheights;
|
||||
prevheights.resize(tx.vin.size());
|
||||
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
|
||||
const CTxIn& txin = tx.vin[txinIndex];
|
||||
CCoins coins;
|
||||
if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) {
|
||||
return error("%s: Missing input", __func__);
|
||||
std::pair<int, int64_t> lockPair;
|
||||
if (useExistingLockPoints) {
|
||||
assert(lp);
|
||||
lockPair.first = lp->height;
|
||||
lockPair.second = lp->time;
|
||||
}
|
||||
else {
|
||||
// pcoinsTip contains the UTXO set for chainActive.Tip()
|
||||
CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
|
||||
std::vector<int> prevheights;
|
||||
prevheights.resize(tx.vin.size());
|
||||
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
|
||||
const CTxIn& txin = tx.vin[txinIndex];
|
||||
CCoins coins;
|
||||
if (!viewMemPool.GetCoins(txin.prevout.hash, coins)) {
|
||||
return error("%s: Missing input", __func__);
|
||||
}
|
||||
if (coins.nHeight == MEMPOOL_HEIGHT) {
|
||||
// Assume all mempool transaction confirm in the next block
|
||||
prevheights[txinIndex] = tip->nHeight + 1;
|
||||
} else {
|
||||
prevheights[txinIndex] = coins.nHeight;
|
||||
}
|
||||
}
|
||||
if (coins.nHeight == MEMPOOL_HEIGHT) {
|
||||
// Assume all mempool transaction confirm in the next block
|
||||
prevheights[txinIndex] = tip->nHeight + 1;
|
||||
} else {
|
||||
prevheights[txinIndex] = coins.nHeight;
|
||||
lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
|
||||
if (lp) {
|
||||
lp->height = lockPair.first;
|
||||
lp->time = lockPair.second;
|
||||
// Also store the hash of the block with the highest height of
|
||||
// all the blocks which have sequence locked prevouts.
|
||||
// This hash needs to still be on the chain
|
||||
// for these LockPoint calculations to be valid
|
||||
// Note: It is impossible to correctly calculate a maxInputBlock
|
||||
// if any of the sequence locked inputs depend on unconfirmed txs,
|
||||
// except in the special case where the relative lock time/height
|
||||
// is 0, which is equivalent to no sequence lock. Since we assume
|
||||
// input height of tip+1 for mempool txs and test the resulting
|
||||
// lockPair from CalculateSequenceLocks against tip+1. We know
|
||||
// EvaluateSequenceLocks will fail if there was a non-zero sequence
|
||||
// lock on a mempool input, so we can use the return value of
|
||||
// CheckSequenceLocks to indicate the LockPoints validity
|
||||
int maxInputHeight = 0;
|
||||
BOOST_FOREACH(int height, prevheights) {
|
||||
// Can ignore mempool inputs since we'll fail if they had non-zero locks
|
||||
if (height != tip->nHeight+1) {
|
||||
maxInputHeight = std::max(maxInputHeight, height);
|
||||
}
|
||||
}
|
||||
lp->maxInputBlock = tip->GetAncestor(maxInputHeight);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int64_t> lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
|
||||
return EvaluateSequenceLocks(index, lockPair);
|
||||
}
|
||||
|
||||
@ -1017,6 +1067,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
CCoinsViewCache view(&dummy);
|
||||
|
||||
CAmount nValueIn = 0;
|
||||
LockPoints lp;
|
||||
{
|
||||
LOCK(pool.cs);
|
||||
CCoinsViewMemPool viewMemPool(pcoinsTip, pool);
|
||||
@ -1060,7 +1111,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
// be mined yet.
|
||||
// Must keep pool.cs for this unless we change CheckSequenceLocks to take a
|
||||
// CoinsViewCache instead of create its own
|
||||
if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
|
||||
if (!CheckSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||
}
|
||||
|
||||
@ -1092,7 +1143,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
|
||||
}
|
||||
}
|
||||
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps);
|
||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps, lp);
|
||||
unsigned int nSize = entry.GetTxSize();
|
||||
|
||||
// Check that the transaction doesn't have an excessive number of
|
||||
|
12
src/main.h
12
src/main.h
@ -39,6 +39,7 @@ class CValidationInterface;
|
||||
class CValidationState;
|
||||
|
||||
struct CNodeStateStats;
|
||||
struct LockPoints;
|
||||
|
||||
/** Default for accepting alerts from the P2P network. */
|
||||
static const bool DEFAULT_ALERTS = true;
|
||||
@ -368,6 +369,11 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
|
||||
*/
|
||||
bool CheckFinalTx(const CTransaction &tx, int flags = -1);
|
||||
|
||||
/**
|
||||
* Test whether the LockPoints height and time are still valid on the current chain
|
||||
*/
|
||||
bool TestLockPointValidity(const LockPoints* lp);
|
||||
|
||||
/**
|
||||
* Check if transaction is final per BIP 68 sequence numbers and can be included in a block.
|
||||
* Consensus critical. Takes as input a list of heights at which tx's inputs (in order) confirmed.
|
||||
@ -378,10 +384,14 @@ bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeig
|
||||
* Check if transaction will be BIP 68 final in the next block to be created.
|
||||
*
|
||||
* Simulates calling SequenceLocks() with data from the tip of the current active chain.
|
||||
* Optionally stores in LockPoints the resulting height and time calculated and the hash
|
||||
* of the block needed for calculation or skips the calculation and uses the LockPoints
|
||||
* passed in for evaluation.
|
||||
* The LockPoints should not be considered valid if CheckSequenceLocks returns false.
|
||||
*
|
||||
* See consensus/consensus.h for flag definitions.
|
||||
*/
|
||||
bool CheckSequenceLocks(const CTransaction &tx, int flags);
|
||||
bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp = NULL, bool useExistingLockPoints = false);
|
||||
|
||||
/**
|
||||
* Closure representing one script verification
|
||||
|
@ -151,7 +151,7 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(CMutableTransaction &tx, CTxMemPo
|
||||
CAmount inChainValue = hasNoDependencies ? txn.GetValueOut() : 0;
|
||||
|
||||
return CTxMemPoolEntry(txn, nFee, nTime, dPriority, nHeight,
|
||||
hasNoDependencies, inChainValue, spendsCoinbase, sigOpCount);
|
||||
hasNoDependencies, inChainValue, spendsCoinbase, sigOpCount, lp);
|
||||
}
|
||||
|
||||
void Shutdown(void* parg)
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "key.h"
|
||||
#include "pubkey.h"
|
||||
#include "txdb.h"
|
||||
#include "txmempool.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
@ -71,7 +72,8 @@ struct TestMemPoolEntryHelper
|
||||
bool hadNoDependencies;
|
||||
bool spendsCoinbase;
|
||||
unsigned int sigOpCount;
|
||||
|
||||
LockPoints lp;
|
||||
|
||||
TestMemPoolEntryHelper() :
|
||||
nFee(0), nTime(0), dPriority(0.0), nHeight(1),
|
||||
hadNoDependencies(false), spendsCoinbase(false), sigOpCount(1) { }
|
||||
|
@ -22,10 +22,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, unsigned int _sigOps):
|
||||
bool _spendsCoinbase, unsigned int _sigOps, LockPoints lp):
|
||||
tx(_tx), nFee(_nFee), nTime(_nTime), entryPriority(_entryPriority), entryHeight(_entryHeight),
|
||||
hadNoDependencies(poolHasNoInputsOf), inChainInputValue(_inChainInputValue),
|
||||
spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps)
|
||||
spendsCoinbase(_spendsCoinbase), sigOpCount(_sigOps), lockPoints(lp)
|
||||
{
|
||||
nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
nModSize = tx.CalculateModifiedSize(nTxSize);
|
||||
@ -61,6 +61,11 @@ void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
|
||||
feeDelta = newFeeDelta;
|
||||
}
|
||||
|
||||
void CTxMemPoolEntry::UpdateLockPoints(const LockPoints& lp)
|
||||
{
|
||||
lockPoints = lp;
|
||||
}
|
||||
|
||||
// Update the given tx for any in-mempool descendants.
|
||||
// Assumes that setMemPoolChildren is correct for the given tx and all
|
||||
// descendants.
|
||||
@ -506,7 +511,11 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
|
||||
list<CTransaction> transactionsToRemove;
|
||||
for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) {
|
||||
const CTransaction& tx = it->GetTx();
|
||||
if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags)) {
|
||||
LockPoints lp = it->GetLockPoints();
|
||||
bool validLP = TestLockPointValidity(&lp);
|
||||
if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags, &lp, validLP)) {
|
||||
// Note if CheckSequenceLocks fails the LockPoints may still be invalid
|
||||
// So it's critical that we remove the tx and not depend on the LockPoints.
|
||||
transactionsToRemove.push_back(tx);
|
||||
} else if (it->GetSpendsCoinbase()) {
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||
@ -521,6 +530,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!validLP) {
|
||||
mapTx.modify(it, update_lock_points(lp));
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH(const CTransaction& tx, transactionsToRemove) {
|
||||
list<CTransaction> removed;
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "boost/multi_index/ordered_index.hpp"
|
||||
|
||||
class CAutoFile;
|
||||
class CBlockIndex;
|
||||
|
||||
inline double AllowFreeThreshold()
|
||||
{
|
||||
@ -35,6 +36,21 @@ inline bool AllowFree(double dPriority)
|
||||
/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */
|
||||
static const unsigned int MEMPOOL_HEIGHT = 0x7FFFFFFF;
|
||||
|
||||
struct LockPoints
|
||||
{
|
||||
// Will be set to the blockchain height and median time past
|
||||
// values that would be necessary to satisfy all relative locktime
|
||||
// constraints (BIP68) of this tx given our view of block chain history
|
||||
int height;
|
||||
int64_t time;
|
||||
// As long as the current chain descends from the highest height block
|
||||
// containing one of the inputs used in the calculation, then the cached
|
||||
// values are still valid even after a reorg.
|
||||
CBlockIndex* maxInputBlock;
|
||||
|
||||
LockPoints() : height(0), time(0), maxInputBlock(NULL) { }
|
||||
};
|
||||
|
||||
class CTxMemPool;
|
||||
|
||||
/** \class CTxMemPoolEntry
|
||||
@ -70,6 +86,7 @@ private:
|
||||
bool spendsCoinbase; //! keep track of transactions that spend a coinbase
|
||||
unsigned int sigOpCount; //! Legacy sig ops plus P2SH sig op count
|
||||
int64_t feeDelta; //! Used for determining the priority of the transaction for mining in a block
|
||||
LockPoints lockPoints; //! Track the height and time at which tx was final
|
||||
|
||||
// Information about descendants of this transaction that are in the
|
||||
// mempool; if we remove this transaction we must remove all of these
|
||||
@ -84,7 +101,7 @@ public:
|
||||
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||
int64_t _nTime, double _entryPriority, unsigned int _entryHeight,
|
||||
bool poolHasNoInputsOf, CAmount _inChainInputValue, bool spendsCoinbase,
|
||||
unsigned int nSigOps);
|
||||
unsigned int nSigOps, LockPoints lp);
|
||||
CTxMemPoolEntry(const CTxMemPoolEntry& other);
|
||||
|
||||
const CTransaction& GetTx() const { return this->tx; }
|
||||
@ -101,12 +118,15 @@ public:
|
||||
unsigned int GetSigOpCount() const { return sigOpCount; }
|
||||
int64_t GetModifiedFee() const { return nFee + feeDelta; }
|
||||
size_t DynamicMemoryUsage() const { return nUsageSize; }
|
||||
const LockPoints& GetLockPoints() const { return lockPoints; }
|
||||
|
||||
// Adjusts the descendant state, if this entry is not dirty.
|
||||
void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
|
||||
// Updates the fee delta used for mining priority score, and the
|
||||
// modified fees with descendants.
|
||||
void UpdateFeeDelta(int64_t feeDelta);
|
||||
// Update the LockPoints after a reorg
|
||||
void UpdateLockPoints(const LockPoints& lp);
|
||||
|
||||
/** We can set the entry to be dirty if doing the full calculation of in-
|
||||
* mempool descendants will be too expensive, which can potentially happen
|
||||
@ -154,6 +174,16 @@ private:
|
||||
int64_t feeDelta;
|
||||
};
|
||||
|
||||
struct update_lock_points
|
||||
{
|
||||
update_lock_points(const LockPoints& _lp) : lp(_lp) { }
|
||||
|
||||
void operator() (CTxMemPoolEntry &e) { e.UpdateLockPoints(lp); }
|
||||
|
||||
private:
|
||||
const LockPoints& lp;
|
||||
};
|
||||
|
||||
// extracts a TxMemPoolEntry's transaction hash
|
||||
struct mempoolentry_txid
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user