From c6c2f0fd782ccf607027414012f45c8f48561a30 Mon Sep 17 00:00:00 2001 From: Alex Morcos Date: Mon, 7 Dec 2015 15:44:16 -0500 Subject: 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 --- src/test/miner_tests.cpp | 126 ++++++++++++++++++++++++++++++++++------------ src/test/script_tests.cpp | 4 +- 2 files changed, 97 insertions(+), 33 deletions(-) (limited to 'src/test') diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 19ddb5b79c..138ba808e3 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -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::vectortxFirst; 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 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)); - - // 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)); + 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 + + // 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--; diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index 0059e4a998..90822c71d7 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -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::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::max(); + txSpend.vin[0].nSequence = CTxIn::SEQUENCE_FINAL; txSpend.vout[0].scriptPubKey = CScript(); txSpend.vout[0].nValue = 0; -- cgit v1.2.3