Implement SequenceLocks functions
SequenceLocks functions are used to evaluate sequence lock times or heights per BIP 68. The majority of this code is copied from maaku in #6312 Further credit: btcdrak, sipa, NicolasDorier
This commit is contained in:
150
src/main.cpp
150
src/main.cpp
@@ -667,9 +667,10 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
|
||||
return true;
|
||||
if ((int64_t)tx.nLockTime < ((int64_t)tx.nLockTime < LOCKTIME_THRESHOLD ? (int64_t)nBlockHeight : nBlockTime))
|
||||
return true;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
if (!txin.IsFinal())
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||
if (!(txin.nSequence == CTxIn::SEQUENCE_FINAL))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -705,6 +706,128 @@ bool CheckFinalTx(const CTransaction &tx, int flags)
|
||||
return IsFinalTx(tx, nBlockHeight, nBlockTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the block height and previous block's median time past at
|
||||
* which the transaction will be considered final in the context of BIP 68.
|
||||
* Also removes from the vector of input heights any entries which did not
|
||||
* correspond to sequence locked inputs as they do not affect the calculation.
|
||||
*/
|
||||
static std::pair<int, int64_t> CalculateSequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
|
||||
{
|
||||
assert(prevHeights->size() == tx.vin.size());
|
||||
|
||||
// Will be set to the equivalent height- and time-based nLockTime
|
||||
// values that would be necessary to satisfy all relative lock-
|
||||
// time constraints given our view of block chain history.
|
||||
// The semantics of nLockTime are the last invalid height/time, so
|
||||
// use -1 to have the effect of any height or time being valid.
|
||||
int nMinHeight = -1;
|
||||
int64_t nMinTime = -1;
|
||||
|
||||
// tx.nVersion is signed integer so requires cast to unsigned otherwise
|
||||
// we would be doing a signed comparison and half the range of nVersion
|
||||
// wouldn't support BIP 68.
|
||||
bool fEnforceBIP68 = static_cast<uint32_t>(tx.nVersion) >= 2
|
||||
&& flags & LOCKTIME_VERIFY_SEQUENCE;
|
||||
|
||||
// Do not enforce sequence numbers as a relative lock time
|
||||
// unless we have been instructed to
|
||||
if (!fEnforceBIP68) {
|
||||
return std::make_pair(nMinHeight, nMinTime);
|
||||
}
|
||||
|
||||
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
|
||||
const CTxIn& txin = tx.vin[txinIndex];
|
||||
|
||||
// Sequence numbers with the most significant bit set are not
|
||||
// treated as relative lock-times, nor are they given any
|
||||
// consensus-enforced meaning at this point.
|
||||
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) {
|
||||
// The height of this input is not relevant for sequence locks
|
||||
(*prevHeights)[txinIndex] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
int nCoinHeight = (*prevHeights)[txinIndex];
|
||||
|
||||
if (txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) {
|
||||
int64_t nCoinTime = block.GetAncestor(std::max(nCoinHeight-1, 0))->GetMedianTimePast();
|
||||
// NOTE: Subtract 1 to maintain nLockTime semantics
|
||||
// BIP 68 relative lock times have the semantics of calculating
|
||||
// the first block or time at which the transaction would be
|
||||
// valid. When calculating the effective block time or height
|
||||
// for the entire transaction, we switch to using the
|
||||
// semantics of nLockTime which is the last invalid block
|
||||
// time or height. Thus we subtract 1 from the calculated
|
||||
// time or height.
|
||||
|
||||
// Time-based relative lock-times are measured from the
|
||||
// smallest allowed timestamp of the block containing the
|
||||
// txout being spent, which is the median time past of the
|
||||
// block prior.
|
||||
nMinTime = std::max(nMinTime, nCoinTime + (int64_t)((txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) << CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) - 1);
|
||||
} else {
|
||||
nMinHeight = std::max(nMinHeight, nCoinHeight + (int)(txin.nSequence & CTxIn::SEQUENCE_LOCKTIME_MASK) - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_pair(nMinHeight, nMinTime);
|
||||
}
|
||||
|
||||
static bool EvaluateSequenceLocks(const CBlockIndex& block, std::pair<int, int64_t> lockPair)
|
||||
{
|
||||
assert(block.pprev);
|
||||
int64_t nBlockTime = block.pprev->GetMedianTimePast();
|
||||
if (lockPair.first >= block.nHeight || lockPair.second >= nBlockTime)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block)
|
||||
{
|
||||
return EvaluateSequenceLocks(block, CalculateSequenceLocks(tx, flags, prevHeights, block));
|
||||
}
|
||||
|
||||
bool CheckSequenceLocks(const CTransaction &tx, int flags)
|
||||
{
|
||||
AssertLockHeld(cs_main);
|
||||
AssertLockHeld(mempool.cs);
|
||||
|
||||
CBlockIndex* tip = chainActive.Tip();
|
||||
CBlockIndex index;
|
||||
index.pprev = tip;
|
||||
// CheckSequenceLocks() uses chainActive.Height()+1 to evaluate
|
||||
// height based locks because when SequenceLocks() is called within
|
||||
// CBlock::AcceptBlock(), the height of the block *being*
|
||||
// evaluated is what is used. Thus if we want to know if a
|
||||
// transaction can be part of the *next* block, we need to call
|
||||
// SequenceLocks() with 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__);
|
||||
}
|
||||
if (coins.nHeight == MEMPOOL_HEIGHT) {
|
||||
// Assume all mempool transaction confirm in the next block
|
||||
prevheights[txinIndex] = tip->nHeight + 1;
|
||||
} else {
|
||||
prevheights[txinIndex] = coins.nHeight;
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<int, int64_t> lockPair = CalculateSequenceLocks(tx, flags, &prevheights, index);
|
||||
return EvaluateSequenceLocks(index, lockPair);
|
||||
}
|
||||
|
||||
|
||||
unsigned int GetLegacySigOpCount(const CTransaction& tx)
|
||||
{
|
||||
unsigned int nSigOps = 0;
|
||||
@@ -949,6 +1072,14 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||
|
||||
// we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool
|
||||
view.SetBackend(dummy);
|
||||
|
||||
// Only accept BIP68 sequence locked transactions that can be mined in the next
|
||||
// block; we don't want our mempool filled up with transactions that can't
|
||||
// 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))
|
||||
return state.DoS(0, false, REJECT_NONSTANDARD, "non-BIP68-final");
|
||||
}
|
||||
|
||||
// Check for non-standard pay-to-script-hash in inputs
|
||||
@@ -2075,6 +2206,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
|
||||
CCheckQueueControl<CScriptCheck> control(fScriptChecks && nScriptCheckThreads ? &scriptcheckqueue : NULL);
|
||||
|
||||
std::vector<int> prevheights;
|
||||
int nLockTimeFlags = 0;
|
||||
CAmount nFees = 0;
|
||||
int nInputs = 0;
|
||||
unsigned int nSigOps = 0;
|
||||
@@ -2098,6 +2231,19 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
return state.DoS(100, error("ConnectBlock(): inputs missing/spent"),
|
||||
REJECT_INVALID, "bad-txns-inputs-missingorspent");
|
||||
|
||||
// Check that transaction is BIP68 final
|
||||
// BIP68 lock checks (as opposed to nLockTime checks) must
|
||||
// be in ConnectBlock because they require the UTXO set
|
||||
prevheights.resize(tx.vin.size());
|
||||
for (size_t j = 0; j < tx.vin.size(); j++) {
|
||||
prevheights[j] = view.AccessCoins(tx.vin[j].prevout.hash)->nHeight;
|
||||
}
|
||||
|
||||
if (!SequenceLocks(tx, nLockTimeFlags, &prevheights, *pindex)) {
|
||||
return state.DoS(100, error("ConnectBlock(): contains a non-BIP68-final transaction", __func__),
|
||||
REJECT_INVALID, "bad-txns-nonfinal");
|
||||
}
|
||||
|
||||
if (fStrictPayToScriptHash)
|
||||
{
|
||||
// Add in sigops done by pay-to-script-hash inputs;
|
||||
|
||||
Reference in New Issue
Block a user