Skip to content

Commit

Permalink
Add rules--presently disabled--for using GetMedianTimePast as endpoin…
Browse files Browse the repository at this point in the history
…t 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.
  • Loading branch information
maaku authored and btcdrak committed Oct 23, 2015
1 parent dfe55bd commit a1d3c6f
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 9 deletions.
9 changes: 9 additions & 0 deletions src/consensus/consensus.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,13 @@ static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
/** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */
static const int COINBASE_MATURITY = 100;

/** Flags for LockTime() */
enum {
/* Use GetMedianTimePast() instead of nTime for end point timestamp. */
LOCKTIME_MEDIAN_TIME_PAST = (1 << 1),
};

/** Used as the flags parameter to CheckFinalTx() in non-consensus code */
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = 0;

#endif // BITCOIN_CONSENSUS_CONSENSUS_H
43 changes: 36 additions & 7 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -709,10 +709,35 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime)
return true;
}

bool CheckFinalTx(const CTransaction &tx)
bool CheckFinalTx(const CTransaction &tx, int flags)
{
AssertLockHeld(cs_main);
return IsFinalTx(tx, chainActive.Height() + 1, GetAdjustedTime());

// By convention a negative value for flags indicates that the
// current network-enforced consensus rules should be used. In
// a future soft-fork scenario that would mean checking which
// rules would be enforced for the next block and setting the
// appropriate flags. At the present time no soft-forks are
// scheduled, so no flags are set.
flags = std::max(flags, 0);

// CheckFinalTx() uses chainActive.Height()+1 to evaluate
// nLockTime because when IsFinalTx() 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
// IsFinalTx() with one more than chainActive.Height().
const int nBlockHeight = chainActive.Height() + 1;

// 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.
// However this changes once median past time-locks are enforced:
const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST)
? chainActive.Tip()->GetMedianTimePast()
: GetAdjustedTime();

return IsFinalTx(tx, nBlockHeight, nBlockTime);
}

/**
Expand Down Expand Up @@ -931,9 +956,8 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa
// 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 (!CheckFinalTx(tx))
return state.DoS(0, error("AcceptToMemoryPool: non-final"),
REJECT_NONSTANDARD, "non-final");
if (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS))
return state.DoS(0, false, REJECT_NONSTANDARD, "non-final");

// is it already in the memory pool?
uint256 hash = tx.GetHash();
Expand Down Expand Up @@ -2809,10 +2833,15 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn
const Consensus::Params& consensusParams = Params().GetConsensus();

// Check that all transactions are finalized
BOOST_FOREACH(const CTransaction& tx, block.vtx)
if (!IsFinalTx(tx, nHeight, block.GetBlockTime())) {
BOOST_FOREACH(const CTransaction& tx, block.vtx) {
int nLockTimeFlags = 0;
int64_t nLockTimeCutoff = (nLockTimeFlags & LOCKTIME_MEDIAN_TIME_PAST)
? pindexPrev->GetMedianTimePast()
: block.GetBlockTime();
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff)) {
return state.DoS(10, error("%s: contains a non-final transaction", __func__), REJECT_INVALID, "bad-txns-nonfinal");
}
}

// Enforce block.nVersion=2 rule that the coinbase starts with serialized block height
// if 750 of the last 1,000 blocks are version 2 or greater (51/100 if testnet):
Expand Down
4 changes: 3 additions & 1 deletion src/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,8 +338,10 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
* Check if transaction will be final in the next block to be created.
*
* Calls IsFinalTx() with current block height and appropriate block time.
*
* See consensus/consensus.h for flag definitions.
*/
bool CheckFinalTx(const CTransaction &tx);
bool CheckFinalTx(const CTransaction &tx, int flags = -1);

/**
* Closure representing one script verification
Expand Down
8 changes: 7 additions & 1 deletion src/miner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,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
Expand All @@ -153,7 +154,12 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
mi != mempool.mapTx.end(); ++mi)
{
const CTransaction& tx = mi->second.GetTx();
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, pblock->nTime))

int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
? nMedianTimePast
: pblock->GetBlockTime();

if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff))
continue;

COrphan* porphan = NULL;
Expand Down

0 comments on commit a1d3c6f

Please sign in to comment.