From 68ea815ad6029dfc245ca6df4d35e2f310d94012 Mon Sep 17 00:00:00 2001 From: Mark Friedenbach Date: Wed, 3 Jun 2015 12:55:45 -0700 Subject: [PATCH] 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. --- src/main.cpp | 16 +++++++++++++--- src/main.h | 3 +++ src/miner.cpp | 9 ++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 65344d51dc..a7e4b55e9f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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"); } diff --git a/src/main.h b/src/main.h index cefebf0418..7ff6460a3b 100644 --- a/src/main.h +++ b/src/main.h @@ -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), }; /** diff --git a/src/miner.cpp b/src/miner.cpp index 722b83473d..bf23e8298d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -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;