@ -57,6 +57,20 @@ struct {
{ 2 , 0xbbbeb305 } , { 2 , 0xfe1c810a } ,
{ 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!
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE ( CreateNewBlock_validity )
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
// We can't make transactions until we have inputs
// Therefore, load 100 blocks :)
// Therefore, load 100 blocks :)
int baseheight = 0 ;
std : : vector < CTransaction * > txFirst ;
std : : vector < CTransaction * > txFirst ;
for ( unsigned int i = 0 ; i < sizeof ( blockinfo ) / sizeof ( * blockinfo ) ; + + i )
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 . vin [ 0 ] . scriptSig . push_back ( chainActive . Height ( ) ) ;
txCoinbase . vout [ 0 ] . scriptPubKey = CScript ( ) ;
txCoinbase . vout [ 0 ] . scriptPubKey = CScript ( ) ;
pblock - > vtx [ 0 ] = CTransaction ( txCoinbase ) ;
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 ] ) ) ;
txFirst . push_back ( new CTransaction ( pblock - > vtx [ 0 ] ) ) ;
pblock - > hashMerkleRoot = BlockMerkleRoot ( * pblock ) ;
pblock - > hashMerkleRoot = BlockMerkleRoot ( * pblock ) ;
pblock - > nNonce = blockinfo [ i ] . nonce ;
pblock - > nNonce = blockinfo [ i ] . nonce ;
@ -240,49 +257,96 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// non-final txs in mempool
// non-final txs in mempool
SetMockTime ( chainActive . Tip ( ) - > GetMedianTimePast ( ) + 1 ) ;
SetMockTime ( chainActive . Tip ( ) - > GetMedianTimePast ( ) + 1 ) ;
int flags = LOCKTIME_VERIFY_SEQUENCE | LOCKTIME_MEDIAN_TIME_PAST ;
// height map
std : : vector < int > prevheights ;
// height locked
// relative height locked
tx . vin [ 0 ] . prevout . hash = txFirst [ 0 ] - > GetHash ( ) ;
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 ] . 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 ] . nValue = 4900000000LL ;
tx . vout [ 0 ] . scriptPubKey = CScript ( ) < < OP_1 ;
tx . vout [ 0 ] . scriptPubKey = CScript ( ) < < OP_1 ;
tx . nLockTime = chainActive . Tip ( ) - > nHeight + 1 ;
tx . nLockTime = 0 ;
hash = tx . GetHash ( ) ;
hash = tx . GetHash ( ) ;
mempool . addUnchecked ( hash , entry . Fee ( 100000000L ) . Time ( GetTime ( ) ) . SpendsCoinbase ( true ) . FromTx ( tx ) ) ;
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
// time locked
BOOST_CHECK ( SequenceLocks ( tx , flags , & prevheights , CreateBlockIndex ( chainActive . Tip ( ) - > nHeight + 2 ) ) ) ; // Sequence locks pass on 2nd block
tx2 . vin . resize ( 1 ) ;
tx2 . vin [ 0 ] . prevout . hash = txFirst [ 1 ] - > GetHash ( ) ;
// relative time locked
tx2 . vin [ 0 ] . prevout . n = 0 ;
tx . vin [ 0 ] . prevout . hash = txFirst [ 1 ] - > GetHash ( ) ;
tx2 . vin [ 0 ] . scriptSig = CScript ( ) < < OP_1 ;
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
tx2 . vin [ 0 ] . nSequence = 0 ;
prevheights [ 0 ] = baseheight + 2 ;
tx2 . vout . resize ( 1 ) ;
hash = tx . GetHash ( ) ;
tx2 . vout [ 0 ] . nValue = 4900000000LL ;
mempool . addUnchecked ( hash , entry . Time ( GetTime ( ) ) . FromTx ( tx ) ) ;
tx2 . vout [ 0 ] . scriptPubKey = CScript ( ) < < OP_1 ;
BOOST_CHECK ( CheckFinalTx ( tx , flags ) ) ; // Locktime passes
tx2 . nLockTime = chainActive . Tip ( ) - > GetMedianTimePast ( ) + 1 ;
BOOST_CHECK ( ! TestSequenceLocks ( tx , flags ) ) ; // Sequence locks fail
hash = tx2 . GetHash ( ) ;
mempool . addUnchecked ( hash , entry . Fee ( 100000000L ) . Time ( GetTime ( ) ) . SpendsCoinbase ( true ) . FromTx ( tx2 ) ) ;
for ( int i = 0 ; i < CBlockIndex : : nMedianTimeSpan ; i + + )
BOOST_CHECK ( ! CheckFinalTx ( tx2 , LOCKTIME_MEDIAN_TIME_PAST ) ) ;
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 ) ) ;
BOOST_CHECK ( pblocktemplate = CreateNewBlock ( chainparams , scriptPubKey ) ) ;
// Neither tx should have make it into the template.
// None of the of the absolute height/time locked tx should have made
BOOST_CHECK_EQUAL ( pblocktemplate - > block . vtx . size ( ) , 1 ) ;
// 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 ;
delete pblocktemplate ;
// However if we advance height by 1 and time by 512, all of them should be mined
// However if we advance height and time by one, both will.
for ( int i = 0 ; i < CBlockIndex : : nMedianTimeSpan ; i + + )
chainActive . Tip ( ) - > GetAncestor ( chainActive . Tip ( ) - > nHeight - i ) - > nTime + = 512 ; //Trick the MedianTimePast
chainActive . Tip ( ) - > nHeight + + ;
chainActive . Tip ( ) - > nHeight + + ;
SetMockTime ( chainActive . Tip ( ) - > GetMedianTimePast ( ) + 2 ) ;
SetMockTime ( chainActive . Tip ( ) - > GetMedianTimePast ( ) + 1 ) ;
// 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));
BOOST_CHECK ( pblocktemplate = CreateNewBlock ( chainparams , scriptPubKey ) ) ;
BOOST_CHECK ( pblocktemplate = CreateNewBlock ( chainparams , scriptPubKey ) ) ;
BOOST_CHECK_EQUAL ( pblocktemplate - > block . vtx . size ( ) , 2 ) ;
BOOST_CHECK_EQUAL ( pblocktemplate - > block . vtx . size ( ) , 5 ) ;
delete pblocktemplate ;
delete pblocktemplate ;
chainActive . Tip ( ) - > nHeight - - ;
chainActive . Tip ( ) - > nHeight - - ;