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:
parent
dc0305d15a
commit
c6c2f0fd78
10 changed files with 300 additions and 49 deletions
|
@ -13,8 +13,11 @@ 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() */
|
||||
/** Flags for nSequence and nLockTime locks */
|
||||
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),
|
||||
};
|
||||
|
|
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;
|
||||
|
|
17
src/main.h
17
src/main.h
|
@ -341,7 +341,22 @@ bool IsFinalTx(const CTransaction &tx, int nBlockHeight, int64_t nBlockTime);
|
|||
*/
|
||||
bool CheckFinalTx(const CTransaction &tx, int flags = -1);
|
||||
|
||||
/**
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
bool SequenceLocks(const CTransaction &tx, int flags, std::vector<int>* prevHeights, const CBlockIndex& block);
|
||||
|
||||
/**
|
||||
* Check if transaction will be BIP 68 final in the next block to be created.
|
||||
*
|
||||
* Calls SequenceLocks() with data from the tip of the current active chain.
|
||||
*
|
||||
* See consensus/consensus.h for flag definitions.
|
||||
*/
|
||||
bool CheckSequenceLocks(const CTransaction &tx, int flags);
|
||||
|
||||
/**
|
||||
* Closure representing one script verification
|
||||
* Note that this stores references to the spending transaction
|
||||
*/
|
||||
|
|
|
@ -45,8 +45,9 @@ static const unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VERIFY
|
|||
/** For convenience, standard but not mandatory verify flags. */
|
||||
static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS;
|
||||
|
||||
/** Used as the flags parameter to CheckFinalTx() in non-consensus code */
|
||||
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_MEDIAN_TIME_PAST;
|
||||
/** Used as the flags parameter to LockTime() in non-consensus code. */
|
||||
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
|
||||
LOCKTIME_MEDIAN_TIME_PAST;
|
||||
|
||||
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
|
||||
/**
|
||||
|
|
|
@ -37,7 +37,7 @@ std::string CTxIn::ToString() const
|
|||
str += strprintf(", coinbase %s", HexStr(scriptSig));
|
||||
else
|
||||
str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24));
|
||||
if (nSequence != std::numeric_limits<unsigned int>::max())
|
||||
if (nSequence != SEQUENCE_FINAL)
|
||||
str += strprintf(", nSequence=%u", nSequence);
|
||||
str += ")";
|
||||
return str;
|
||||
|
|
|
@ -61,13 +61,40 @@ public:
|
|||
CScript scriptSig;
|
||||
uint32_t nSequence;
|
||||
|
||||
/* Setting nSequence to this value for every input in a transaction
|
||||
* disables nLockTime. */
|
||||
static const uint32_t SEQUENCE_FINAL = 0xffffffff;
|
||||
|
||||
/* Below flags apply in the context of BIP 68*/
|
||||
/* If this flag set, CTxIn::nSequence is NOT interpreted as a
|
||||
* relative lock-time. */
|
||||
static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31);
|
||||
|
||||
/* If CTxIn::nSequence encodes a relative lock-time and this flag
|
||||
* is set, the relative lock-time has units of 512 seconds,
|
||||
* otherwise it specifies blocks with a granularity of 1. */
|
||||
static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22);
|
||||
|
||||
/* If CTxIn::nSequence encodes a relative lock-time, this mask is
|
||||
* applied to extract that lock-time from the sequence field. */
|
||||
static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff;
|
||||
|
||||
/* In order to use the same number of bits to encode roughly the
|
||||
* same wall-clock duration, and because blocks are naturally
|
||||
* limited to occur every 600s on average, the minimum granularity
|
||||
* for time-based relative lock-time is fixed at 512 seconds.
|
||||
* Converting from CTxIn::nSequence to seconds is performed by
|
||||
* multiplying by 512 = 2^9, or equivalently shifting up by
|
||||
* 9 bits. */
|
||||
static const int SEQUENCE_LOCKTIME_GRANULARITY = 9;
|
||||
|
||||
CTxIn()
|
||||
{
|
||||
nSequence = std::numeric_limits<unsigned int>::max();
|
||||
nSequence = SEQUENCE_FINAL;
|
||||
}
|
||||
|
||||
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits<unsigned int>::max());
|
||||
CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=std::numeric_limits<uint32_t>::max());
|
||||
explicit CTxIn(COutPoint prevoutIn, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
|
||||
CTxIn(uint256 hashPrevTx, uint32_t nOut, CScript scriptSigIn=CScript(), uint32_t nSequenceIn=SEQUENCE_FINAL);
|
||||
|
||||
ADD_SERIALIZE_METHODS;
|
||||
|
||||
|
@ -78,11 +105,6 @@ public:
|
|||
READWRITE(nSequence);
|
||||
}
|
||||
|
||||
bool IsFinal() const
|
||||
{
|
||||
return (nSequence == std::numeric_limits<uint32_t>::max());
|
||||
}
|
||||
|
||||
friend bool operator==(const CTxIn& a, const CTxIn& b)
|
||||
{
|
||||
return (a.prevout == b.prevout &&
|
||||
|
|
|
@ -1147,7 +1147,7 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con
|
|||
// prevent this condition. Alternatively we could test all
|
||||
// inputs, but testing just this input minimizes the data
|
||||
// required to prove correct CHECKLOCKTIMEVERIFY execution.
|
||||
if (txTo->vin[nIn].IsFinal())
|
||||
if (CTxIn::SEQUENCE_FINAL == txTo->vin[nIn].nSequence)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -57,6 +57,20 @@ struct {
|
|||
{2, 0xbbbeb305}, {2, 0xfe1c810a},
|
||||
};
|
||||
|
||||
CBlockIndex CreateBlockIndex(int nHeight)
|
||||
{
|
||||
CBlockIndex index;
|
||||
index.nHeight = nHeight;
|
||||
index.pprev = chainActive.Tip();
|
||||
return index;
|
||||
}
|
||||
|
||||
bool TestSequenceLocks(const CTransaction &tx, int flags)
|
||||
{
|
||||
LOCK(mempool.cs);
|
||||
return CheckSequenceLocks(tx, flags);
|
||||
}
|
||||
|
||||
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
|
||||
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
||||
{
|
||||
|
@ -79,6 +93,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
|
||||
// We can't make transactions until we have inputs
|
||||
// Therefore, load 100 blocks :)
|
||||
int baseheight = 0;
|
||||
std::vector<CTransaction*>txFirst;
|
||||
for (unsigned int i = 0; i < sizeof(blockinfo)/sizeof(*blockinfo); ++i)
|
||||
{
|
||||
|
@ -92,7 +107,9 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
|
||||
txCoinbase.vout[0].scriptPubKey = CScript();
|
||||
pblock->vtx[0] = CTransaction(txCoinbase);
|
||||
if (txFirst.size() < 2)
|
||||
if (txFirst.size() == 0)
|
||||
baseheight = chainActive.Height();
|
||||
if (txFirst.size() < 4)
|
||||
txFirst.push_back(new CTransaction(pblock->vtx[0]));
|
||||
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
|
||||
pblock->nNonce = blockinfo[i].nonce;
|
||||
|
@ -240,49 +257,96 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
|
|||
|
||||
// non-final txs in mempool
|
||||
SetMockTime(chainActive.Tip()->GetMedianTimePast()+1);
|
||||
int flags = LOCKTIME_VERIFY_SEQUENCE|LOCKTIME_MEDIAN_TIME_PAST;
|
||||
// height map
|
||||
std::vector<int> prevheights;
|
||||
|
||||
// height locked
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
|
||||
// relative height locked
|
||||
tx.nVersion = 2;
|
||||
tx.vin.resize(1);
|
||||
prevheights.resize(1);
|
||||
tx.vin[0].prevout.hash = txFirst[0]->GetHash(); // only 1 transaction
|
||||
tx.vin[0].prevout.n = 0;
|
||||
tx.vin[0].scriptSig = CScript() << OP_1;
|
||||
tx.vin[0].nSequence = 0;
|
||||
tx.vin[0].nSequence = chainActive.Tip()->nHeight + 1; // txFirst[0] is the 2nd block
|
||||
prevheights[0] = baseheight + 1;
|
||||
tx.vout.resize(1);
|
||||
tx.vout[0].nValue = 4900000000LL;
|
||||
tx.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
tx.nLockTime = chainActive.Tip()->nHeight+1;
|
||||
tx.nLockTime = 0;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
|
||||
BOOST_CHECK(!CheckFinalTx(tx, LOCKTIME_MEDIAN_TIME_PAST));
|
||||
BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes
|
||||
BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
|
||||
BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
|
||||
|
||||
// time locked
|
||||
tx2.vin.resize(1);
|
||||
tx2.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx2.vin[0].prevout.n = 0;
|
||||
tx2.vin[0].scriptSig = CScript() << OP_1;
|
||||
tx2.vin[0].nSequence = 0;
|
||||
tx2.vout.resize(1);
|
||||
tx2.vout[0].nValue = 4900000000LL;
|
||||
tx2.vout[0].scriptPubKey = CScript() << OP_1;
|
||||
tx2.nLockTime = chainActive.Tip()->GetMedianTimePast()+1;
|
||||
hash = tx2.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Fee(100000000L).Time(GetTime()).SpendsCoinbase(true).FromTx(tx2));
|
||||
BOOST_CHECK(!CheckFinalTx(tx2, LOCKTIME_MEDIAN_TIME_PAST));
|
||||
// relative time locked
|
||||
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
|
||||
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((chainActive.Tip()->GetMedianTimePast()+1-chainActive[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
|
||||
prevheights[0] = baseheight + 2;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes
|
||||
BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
|
||||
|
||||
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
|
||||
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
|
||||
BOOST_CHECK(SequenceLocks(tx, flags, &prevheights, CreateBlockIndex(chainActive.Tip()->nHeight + 1))); // Sequence locks pass 512 seconds later
|
||||
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
|
||||
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime -= 512; //undo tricked MTP
|
||||
|
||||
// absolute height locked
|
||||
tx.vin[0].prevout.hash = txFirst[2]->GetHash();
|
||||
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
|
||||
prevheights[0] = baseheight + 3;
|
||||
tx.nLockTime = chainActive.Tip()->nHeight + 1;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(!CheckFinalTx(tx, flags)); // Locktime fails
|
||||
BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
|
||||
BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
|
||||
|
||||
// absolute time locked
|
||||
tx.vin[0].prevout.hash = txFirst[3]->GetHash();
|
||||
tx.nLockTime = chainActive.Tip()->GetMedianTimePast();
|
||||
prevheights.resize(1);
|
||||
prevheights[0] = baseheight + 4;
|
||||
hash = tx.GetHash();
|
||||
mempool.addUnchecked(hash, entry.Time(GetTime()).FromTx(tx));
|
||||
BOOST_CHECK(!CheckFinalTx(tx, flags)); // Locktime fails
|
||||
BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
|
||||
BOOST_CHECK(IsFinalTx(tx, chainActive.Tip()->nHeight + 2, chainActive.Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
|
||||
|
||||
// mempool-dependent transactions (not added)
|
||||
tx.vin[0].prevout.hash = hash;
|
||||
prevheights[0] = chainActive.Tip()->nHeight + 1;
|
||||
tx.nLockTime = 0;
|
||||
tx.vin[0].nSequence = 0;
|
||||
BOOST_CHECK(CheckFinalTx(tx, flags)); // Locktime passes
|
||||
BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
|
||||
tx.vin[0].nSequence = 1;
|
||||
BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
|
||||
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
|
||||
BOOST_CHECK(TestSequenceLocks(tx, flags)); // Sequence locks pass
|
||||
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
|
||||
BOOST_CHECK(!TestSequenceLocks(tx, flags)); // Sequence locks fail
|
||||
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
|
||||
// Neither tx should have make it into the template.
|
||||
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 1);
|
||||
// None of the of the absolute height/time locked tx should have made
|
||||
// it into the template because we still check IsFinalTx in CreateNewBlock,
|
||||
// but relative locked txs will if inconsistently added to mempool.
|
||||
// For now these will still generate a valid template until BIP68 soft fork
|
||||
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3);
|
||||
delete pblocktemplate;
|
||||
|
||||
// However if we advance height and time by one, both will.
|
||||
// However if we advance height by 1 and time by 512, all of them should be mined
|
||||
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++)
|
||||
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime += 512; //Trick the MedianTimePast
|
||||
chainActive.Tip()->nHeight++;
|
||||
SetMockTime(chainActive.Tip()->GetMedianTimePast()+2);
|
||||
|
||||
// FIXME: we should *actually* create a new block so the following test
|
||||
// works; CheckFinalTx() isn't fooled by monkey-patching nHeight.
|
||||
//BOOST_CHECK(CheckFinalTx(tx));
|
||||
//BOOST_CHECK(CheckFinalTx(tx2));
|
||||
SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1);
|
||||
|
||||
BOOST_CHECK(pblocktemplate = CreateNewBlock(chainparams, scriptPubKey));
|
||||
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 2);
|
||||
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5);
|
||||
delete pblocktemplate;
|
||||
|
||||
chainActive.Tip()->nHeight--;
|
||||
|
|
|
@ -63,7 +63,7 @@ CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey)
|
|||
txCredit.vout.resize(1);
|
||||
txCredit.vin[0].prevout.SetNull();
|
||||
txCredit.vin[0].scriptSig = CScript() << CScriptNum(0) << CScriptNum(0);
|
||||
txCredit.vin[0].nSequence = std::numeric_limits<unsigned int>::max();
|
||||
txCredit.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
|
||||
txCredit.vout[0].scriptPubKey = scriptPubKey;
|
||||
txCredit.vout[0].nValue = 0;
|
||||
|
||||
|
@ -80,7 +80,7 @@ CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CMu
|
|||
txSpend.vin[0].prevout.hash = txCredit.GetHash();
|
||||
txSpend.vin[0].prevout.n = 0;
|
||||
txSpend.vin[0].scriptSig = scriptSig;
|
||||
txSpend.vin[0].nSequence = std::numeric_limits<unsigned int>::max();
|
||||
txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL;
|
||||
txSpend.vout[0].scriptPubKey = CScript();
|
||||
txSpend.vout[0].nValue = 0;
|
||||
|
||||
|
|
|
@ -504,7 +504,7 @@ 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)) {
|
||||
if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(tx, flags)) {
|
||||
transactionsToRemove.push_back(tx);
|
||||
} else if (it->GetSpendsCoinbase()) {
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin) {
|
||||
|
|
Loading…
Reference in a new issue