Add rules--presently disabled--for using GetMedianTimePast as endpoint for lock-time calculations

The lock-time code currently uses CBlock::nTime as the cutoff point for time based locked transactions. This has the unfortunate outcome of creating a perverse incentive for miners to lie about the time of a block in order to collect more fees by including transactions that by wall clock determination have not yet matured. By using CBlockIndex::GetMedianTimePast from the prior block instead, the self-interested miner no longer gains from generating blocks with fraudulent timestamps. Users can compensate for this change by simply adding an hour (3600 seconds) to their time-based lock times.

If enforced, this would be a soft-fork change. This commit only adds the functionality on an unexecuted code path, without changing the behaviour of Bitcoin Core.
This commit is contained in:
Mark Friedenbach 2015-06-03 12:55:45 -07:00
parent 0d96b81945
commit 68ea815ad6
3 changed files with 24 additions and 4 deletions

View file

@ -775,7 +775,9 @@ int64_t CheckLockTime(const CTransaction &tx, int flags)
// Timestamps on the other hand don't get any special treatment,
// because we can't know what timestamp the next block will have,
// and there aren't timestamp applications where it matters.
const int64_t nBlockTime = GetAdjustedTime();
int64_t nBlockTime = GetAdjustedTime();
if (flags & LOCKTIME_MEDIAN_TIME_PAST)
nBlockTime -= ((CBlockIndex::nMedianTimeSpan + 1) >> 1) * Params().GetConsensus().nPowTargetSpacing;
return LockTime(tx, flags, pCoinsView, nBlockHeight, nBlockTime);
}
@ -1051,10 +1053,15 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
view.SetBackend(dummy);
}
int nLockTimeFlags = 0;
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
? chainActive.Tip()->GetMedianTimePast()
: GetAdjustedTime();
// Only accept nLockTime-using 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.
if (LockTime(tx, 0, &view, chainActive.Height() + 1, GetAdjustedTime()))
if (LockTime(tx, nLockTimeFlags, &view, chainActive.Height() + 1, nLockTimeCutoff))
return state.DoS(0, error("AcceptToMemoryPool: non-final"),
REJECT_NONSTANDARD, "non-final");
@ -2862,7 +2869,10 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
int nLockTimeFlags = 0;
if (LockTime(tx, nLockTimeFlags, pcoinsTip, nHeight, block.GetBlockTime()))
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
? pindexPrev->GetMedianTimePast()
: block.GetBlockTime();
if (LockTime(tx, nLockTimeFlags, pcoinsTip, nHeight, nLockTimeCutoff))
return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
}

View file

@ -331,6 +331,9 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason);
enum {
/* Interpret sequence numbers as relative lock-time constraints. */
LOCKTIME_VERIFY_SEQUENCE = (1 << 0),
/* Use GetMedianTimePast() instead of nTime for end point timestamp. */
LOCKTIME_MEDIAN_TIME_PAST = (1 << 1),
};
/**

View file

@ -145,6 +145,7 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
CBlockIndex* pindexPrev = chainActive.Tip();
const int nHeight = pindexPrev->nHeight + 1;
pblock->nTime = GetAdjustedTime();
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
CCoinsViewCache view(pcoinsTip);
// Priority order to process transactions
@ -159,7 +160,13 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
mi != mempool.mapTx.end(); ++mi)
{
const CTransaction& tx = mi->second.GetTx();
if (tx.IsCoinBase() || LockTime(tx, 0, &view, nHeight, pblock->nTime))
int nLockTimeFlags = 0;
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
? nMedianTimePast
: pblock->GetBlockTime();
if (tx.IsCoinBase() || LockTime(tx, nLockTimeFlags, &view, nHeight, nLockTimeCutoff))
continue;
COrphan* porphan = NULL;