github.com/btcsuite/btcd@v0.24.0/integration/csv_fork_test.go (about)

     1  // Copyright (c) 2016 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  // This file is ignored during the regular tests due to the following build tag.
     6  //go:build rpctest
     7  // +build rpctest
     8  
     9  package integration
    10  
    11  import (
    12  	"bytes"
    13  	"runtime"
    14  	"strings"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/btcsuite/btcd/blockchain"
    19  	"github.com/btcsuite/btcd/btcec/v2"
    20  	"github.com/btcsuite/btcd/btcutil"
    21  	"github.com/btcsuite/btcd/chaincfg"
    22  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    23  	"github.com/btcsuite/btcd/integration/rpctest"
    24  	"github.com/btcsuite/btcd/txscript"
    25  	"github.com/btcsuite/btcd/wire"
    26  )
    27  
    28  const (
    29  	csvKey = "csv"
    30  )
    31  
    32  // makeTestOutput creates an on-chain output paying to a freshly generated
    33  // p2pkh output with the specified amount.
    34  func makeTestOutput(r *rpctest.Harness, t *testing.T,
    35  	amt btcutil.Amount) (*btcec.PrivateKey, *wire.OutPoint, []byte, error) {
    36  
    37  	// Create a fresh key, then send some coins to an address spendable by
    38  	// that key.
    39  	key, err := btcec.NewPrivateKey()
    40  	if err != nil {
    41  		return nil, nil, nil, err
    42  	}
    43  
    44  	// Using the key created above, generate a pkScript which it's able to
    45  	// spend.
    46  	a, err := btcutil.NewAddressPubKey(key.PubKey().SerializeCompressed(), r.ActiveNet)
    47  	if err != nil {
    48  		return nil, nil, nil, err
    49  	}
    50  	selfAddrScript, err := txscript.PayToAddrScript(a.AddressPubKeyHash())
    51  	if err != nil {
    52  		return nil, nil, nil, err
    53  	}
    54  	output := &wire.TxOut{PkScript: selfAddrScript, Value: 1e8}
    55  
    56  	// Next, create and broadcast a transaction paying to the output.
    57  	fundTx, err := r.CreateTransaction([]*wire.TxOut{output}, 10, true)
    58  	if err != nil {
    59  		return nil, nil, nil, err
    60  	}
    61  	txHash, err := r.Client.SendRawTransaction(fundTx, true)
    62  	if err != nil {
    63  		return nil, nil, nil, err
    64  	}
    65  
    66  	// The transaction created above should be included within the next
    67  	// generated block.
    68  	blockHash, err := r.Client.Generate(1)
    69  	if err != nil {
    70  		return nil, nil, nil, err
    71  	}
    72  	assertTxInBlock(r, t, blockHash[0], txHash)
    73  
    74  	// Locate the output index of the coins spendable by the key we
    75  	// generated above, this is needed in order to create a proper utxo for
    76  	// this output.
    77  	var outputIndex uint32
    78  	if bytes.Equal(fundTx.TxOut[0].PkScript, selfAddrScript) {
    79  		outputIndex = 0
    80  	} else {
    81  		outputIndex = 1
    82  	}
    83  
    84  	utxo := &wire.OutPoint{
    85  		Hash:  fundTx.TxHash(),
    86  		Index: outputIndex,
    87  	}
    88  
    89  	return key, utxo, selfAddrScript, nil
    90  }
    91  
    92  // TestBIP0113Activation tests for proper adherence of the BIP 113 rule
    93  // constraint which requires all transaction finality tests to use the MTP of
    94  // the last 11 blocks, rather than the timestamp of the block which includes
    95  // them.
    96  //
    97  // Overview:
    98  //
    99  // - Pre soft-fork:
   100  //  1. Transactions with non-final lock-times from the PoV of MTP should be
   101  //     rejected from the mempool.
   102  //  2. Transactions within non-final MTP based lock-times should be accepted
   103  //     in valid blocks.
   104  //
   105  // - Post soft-fork:
   106  //  1. Transactions with non-final lock-times from the PoV of MTP should be
   107  //     rejected from the mempool and when found within otherwise valid blocks.
   108  //  2. Transactions with final lock-times from the PoV of MTP should be
   109  //     accepted to the mempool and mined in future block.
   110  func TestBIP0113Activation(t *testing.T) {
   111  	t.Parallel()
   112  
   113  	btcdCfg := []string{"--rejectnonstd"}
   114  	r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "")
   115  	if err != nil {
   116  		t.Fatal("unable to create primary harness: ", err)
   117  	}
   118  	if err := r.SetUp(true, 1); err != nil {
   119  		t.Fatalf("unable to setup test chain: %v", err)
   120  	}
   121  	defer r.TearDown()
   122  
   123  	// Create a fresh output for usage within the test below.
   124  	const outputValue = btcutil.SatoshiPerBitcoin
   125  	outputKey, testOutput, testPkScript, err := makeTestOutput(r, t,
   126  		outputValue)
   127  	if err != nil {
   128  		t.Fatalf("unable to create test output: %v", err)
   129  	}
   130  
   131  	// Fetch a fresh address from the harness, we'll use this address to
   132  	// send funds back into the Harness.
   133  	addr, err := r.NewAddress()
   134  	if err != nil {
   135  		t.Fatalf("unable to generate address: %v", err)
   136  	}
   137  	addrScript, err := txscript.PayToAddrScript(addr)
   138  	if err != nil {
   139  		t.Fatalf("unable to generate addr script: %v", err)
   140  	}
   141  
   142  	// Now create a transaction with a lock time which is "final" according
   143  	// to the latest block, but not according to the current median time
   144  	// past.
   145  	tx := wire.NewMsgTx(1)
   146  	tx.AddTxIn(&wire.TxIn{
   147  		PreviousOutPoint: *testOutput,
   148  	})
   149  	tx.AddTxOut(&wire.TxOut{
   150  		PkScript: addrScript,
   151  		Value:    outputValue - 1000,
   152  	})
   153  
   154  	// We set the lock-time of the transaction to just one minute after the
   155  	// current MTP of the chain.
   156  	chainInfo, err := r.Client.GetBlockChainInfo()
   157  	if err != nil {
   158  		t.Fatalf("unable to query for chain info: %v", err)
   159  	}
   160  	tx.LockTime = uint32(chainInfo.MedianTime) + 1
   161  
   162  	sigScript, err := txscript.SignatureScript(tx, 0, testPkScript,
   163  		txscript.SigHashAll, outputKey, true)
   164  	if err != nil {
   165  		t.Fatalf("unable to generate sig: %v", err)
   166  	}
   167  	tx.TxIn[0].SignatureScript = sigScript
   168  
   169  	// This transaction should be rejected from the mempool as using MTP
   170  	// for transactions finality is now a policy rule. Additionally, the
   171  	// exact error should be the rejection of a non-final transaction.
   172  	_, err = r.Client.SendRawTransaction(tx, true)
   173  	if err == nil {
   174  		t.Fatalf("transaction accepted, but should be non-final")
   175  	} else if !strings.Contains(err.Error(), "not finalized") {
   176  		t.Fatalf("transaction should be rejected due to being "+
   177  			"non-final, instead: %v", err)
   178  	}
   179  
   180  	// However, since the block validation consensus rules haven't yet
   181  	// activated, a block including the transaction should be accepted.
   182  	txns := []*btcutil.Tx{btcutil.NewTx(tx)}
   183  	block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
   184  	if err != nil {
   185  		t.Fatalf("unable to submit block: %v", err)
   186  	}
   187  	txid := tx.TxHash()
   188  	assertTxInBlock(r, t, block.Hash(), &txid)
   189  
   190  	// At this point, the block height should be 103: we mined 101 blocks
   191  	// to create a single mature output, then an additional block to create
   192  	// a new output, and then mined a single block above to include our
   193  	// transaction.
   194  	assertChainHeight(r, t, 103)
   195  
   196  	// Next, mine enough blocks to ensure that the soft-fork becomes
   197  	// activated. Assert that the block version of the second-to-last block
   198  	// in the final range is active.
   199  
   200  	// Next, mine ensure blocks to ensure that the soft-fork becomes
   201  	// active. We're at height 103 and we need 200 blocks to be mined after
   202  	// the genesis target period, so we mine 196 blocks. This'll put us at
   203  	// height 299. The getblockchaininfo call checks the state for the
   204  	// block AFTER the current height.
   205  	numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 4
   206  	if _, err := r.Client.Generate(numBlocks); err != nil {
   207  		t.Fatalf("unable to generate blocks: %v", err)
   208  	}
   209  
   210  	assertChainHeight(r, t, 299)
   211  	assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive)
   212  
   213  	// The timeLockDeltas slice represents a series of deviations from the
   214  	// current MTP which will be used to test border conditions w.r.t
   215  	// transaction finality. -1 indicates 1 second prior to the MTP, 0
   216  	// indicates the current MTP, and 1 indicates 1 second after the
   217  	// current MTP.
   218  	//
   219  	// This time, all transactions which are final according to the MTP
   220  	// *should* be accepted to both the mempool and within a valid block.
   221  	// While transactions with lock-times *after* the current MTP should be
   222  	// rejected.
   223  	timeLockDeltas := []int64{-1, 0, 1}
   224  	for _, timeLockDelta := range timeLockDeltas {
   225  		chainInfo, err = r.Client.GetBlockChainInfo()
   226  		if err != nil {
   227  			t.Fatalf("unable to query for chain info: %v", err)
   228  		}
   229  		medianTimePast := chainInfo.MedianTime
   230  
   231  		// Create another test output to be spent shortly below.
   232  		outputKey, testOutput, testPkScript, err = makeTestOutput(r, t,
   233  			outputValue)
   234  		if err != nil {
   235  			t.Fatalf("unable to create test output: %v", err)
   236  		}
   237  
   238  		// Create a new transaction with a lock-time past the current known
   239  		// MTP.
   240  		tx = wire.NewMsgTx(1)
   241  		tx.AddTxIn(&wire.TxIn{
   242  			PreviousOutPoint: *testOutput,
   243  		})
   244  		tx.AddTxOut(&wire.TxOut{
   245  			PkScript: addrScript,
   246  			Value:    outputValue - 1000,
   247  		})
   248  		tx.LockTime = uint32(medianTimePast + timeLockDelta)
   249  		sigScript, err = txscript.SignatureScript(tx, 0, testPkScript,
   250  			txscript.SigHashAll, outputKey, true)
   251  		if err != nil {
   252  			t.Fatalf("unable to generate sig: %v", err)
   253  		}
   254  		tx.TxIn[0].SignatureScript = sigScript
   255  
   256  		// If the time-lock delta is greater than -1, then the
   257  		// transaction should be rejected from the mempool and when
   258  		// included within a block. A time-lock delta of -1 should be
   259  		// accepted as it has a lock-time of one
   260  		// second _before_ the current MTP.
   261  
   262  		_, err = r.Client.SendRawTransaction(tx, true)
   263  		if err == nil && timeLockDelta >= 0 {
   264  			t.Fatal("transaction was accepted into the mempool " +
   265  				"but should be rejected!")
   266  		} else if err != nil && !strings.Contains(err.Error(), "not finalized") {
   267  			t.Fatalf("transaction should be rejected from mempool "+
   268  				"due to being  non-final, instead: %v", err)
   269  		}
   270  
   271  		txns = []*btcutil.Tx{btcutil.NewTx(tx)}
   272  		_, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
   273  		if err == nil && timeLockDelta >= 0 {
   274  			t.Fatal("block should be rejected due to non-final " +
   275  				"txn, but was accepted")
   276  		} else if err != nil && !strings.Contains(err.Error(), "unfinalized") {
   277  			t.Fatalf("block should be rejected due to non-final "+
   278  				"tx, instead: %v", err)
   279  		}
   280  	}
   281  }
   282  
   283  // createCSVOutput creates an output paying to a trivially redeemable CSV
   284  // pkScript with the specified time-lock.
   285  func createCSVOutput(r *rpctest.Harness, t *testing.T,
   286  	numSatoshis btcutil.Amount, timeLock int32,
   287  	isSeconds bool) ([]byte, *wire.OutPoint, *wire.MsgTx, error) {
   288  
   289  	// Convert the time-lock to the proper sequence lock based according to
   290  	// if the lock is seconds or time based.
   291  	sequenceLock := blockchain.LockTimeToSequence(isSeconds,
   292  		uint32(timeLock))
   293  
   294  	// Our CSV script is simply: <sequenceLock> OP_CSV OP_DROP
   295  	b := txscript.NewScriptBuilder().
   296  		AddInt64(int64(sequenceLock)).
   297  		AddOp(txscript.OP_CHECKSEQUENCEVERIFY).
   298  		AddOp(txscript.OP_DROP)
   299  	csvScript, err := b.Script()
   300  	if err != nil {
   301  		return nil, nil, nil, err
   302  	}
   303  
   304  	// Using the script generated above, create a P2SH output which will be
   305  	// accepted into the mempool.
   306  	p2shAddr, err := btcutil.NewAddressScriptHash(csvScript, r.ActiveNet)
   307  	if err != nil {
   308  		return nil, nil, nil, err
   309  	}
   310  	p2shScript, err := txscript.PayToAddrScript(p2shAddr)
   311  	if err != nil {
   312  		return nil, nil, nil, err
   313  	}
   314  	output := &wire.TxOut{
   315  		PkScript: p2shScript,
   316  		Value:    int64(numSatoshis),
   317  	}
   318  
   319  	// Finally create a valid transaction which creates the output crafted
   320  	// above.
   321  	tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10, true)
   322  	if err != nil {
   323  		return nil, nil, nil, err
   324  	}
   325  
   326  	var outputIndex uint32
   327  	if !bytes.Equal(tx.TxOut[0].PkScript, p2shScript) {
   328  		outputIndex = 1
   329  	}
   330  
   331  	utxo := &wire.OutPoint{
   332  		Hash:  tx.TxHash(),
   333  		Index: outputIndex,
   334  	}
   335  
   336  	return csvScript, utxo, tx, nil
   337  }
   338  
   339  // spendCSVOutput spends an output previously created by the createCSVOutput
   340  // function. The sigScript is a trivial push of OP_TRUE followed by the
   341  // redeemScript to pass P2SH evaluation.
   342  func spendCSVOutput(redeemScript []byte, csvUTXO *wire.OutPoint,
   343  	sequence uint32, targetOutput *wire.TxOut,
   344  	txVersion int32) (*wire.MsgTx, error) {
   345  
   346  	tx := wire.NewMsgTx(txVersion)
   347  	tx.AddTxIn(&wire.TxIn{
   348  		PreviousOutPoint: *csvUTXO,
   349  		Sequence:         sequence,
   350  	})
   351  	tx.AddTxOut(targetOutput)
   352  
   353  	b := txscript.NewScriptBuilder().
   354  		AddOp(txscript.OP_TRUE).
   355  		AddData(redeemScript)
   356  
   357  	sigScript, err := b.Script()
   358  	if err != nil {
   359  		return nil, err
   360  	}
   361  	tx.TxIn[0].SignatureScript = sigScript
   362  
   363  	return tx, nil
   364  }
   365  
   366  // assertTxInBlock asserts a transaction with the specified txid is found
   367  // within the block with the passed block hash.
   368  func assertTxInBlock(r *rpctest.Harness, t *testing.T, blockHash *chainhash.Hash,
   369  	txid *chainhash.Hash) {
   370  
   371  	block, err := r.Client.GetBlock(blockHash)
   372  	if err != nil {
   373  		t.Fatalf("unable to get block: %v", err)
   374  	}
   375  	if len(block.Transactions) < 2 {
   376  		t.Fatal("target transaction was not mined")
   377  	}
   378  
   379  	for _, txn := range block.Transactions {
   380  		txHash := txn.TxHash()
   381  		if txn.TxHash() == txHash {
   382  			return
   383  		}
   384  	}
   385  
   386  	_, _, line, _ := runtime.Caller(1)
   387  	t.Fatalf("assertion failed at line %v: txid %v was not found in "+
   388  		"block %v", line, txid, blockHash)
   389  }
   390  
   391  // TestBIP0068AndBIP0112Activation tests for the proper adherence to the BIP
   392  // 112 and BIP 68 rule-set after the activation of the CSV-package soft-fork.
   393  //
   394  // Overview:
   395  // - Pre soft-fork:
   396  //  1. A transaction spending a CSV output validly should be rejected from the
   397  //     mempool, but accepted in a valid generated block including the
   398  //     transaction.
   399  //
   400  // - Post soft-fork:
   401  //  1. See the cases exercised within the table driven tests towards the end
   402  //     of this test.
   403  func TestBIP0068AndBIP0112Activation(t *testing.T) {
   404  	t.Parallel()
   405  
   406  	// We'd like the test proper evaluation and validation of the BIP 68
   407  	// (sequence locks) and BIP 112 rule-sets which add input-age based
   408  	// relative lock times.
   409  
   410  	btcdCfg := []string{"--rejectnonstd"}
   411  	r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg, "")
   412  	if err != nil {
   413  		t.Fatal("unable to create primary harness: ", err)
   414  	}
   415  	if err := r.SetUp(true, 1); err != nil {
   416  		t.Fatalf("unable to setup test chain: %v", err)
   417  	}
   418  	defer r.TearDown()
   419  
   420  	assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdStarted)
   421  
   422  	harnessAddr, err := r.NewAddress()
   423  	if err != nil {
   424  		t.Fatalf("unable to obtain harness address: %v", err)
   425  	}
   426  	harnessScript, err := txscript.PayToAddrScript(harnessAddr)
   427  	if err != nil {
   428  		t.Fatalf("unable to generate pkScript: %v", err)
   429  	}
   430  
   431  	const (
   432  		outputAmt         = btcutil.SatoshiPerBitcoin
   433  		relativeBlockLock = 10
   434  	)
   435  
   436  	sweepOutput := &wire.TxOut{
   437  		Value:    outputAmt - 5000,
   438  		PkScript: harnessScript,
   439  	}
   440  
   441  	// As the soft-fork hasn't yet activated _any_ transaction version
   442  	// which uses the CSV opcode should be accepted. Since at this point,
   443  	// CSV doesn't actually exist, it's just a NOP.
   444  	for txVersion := int32(0); txVersion < 3; txVersion++ {
   445  		// Create a trivially spendable output with a CSV lock-time of
   446  		// 10 relative blocks.
   447  		redeemScript, testUTXO, tx, err := createCSVOutput(r, t, outputAmt,
   448  			relativeBlockLock, false)
   449  		if err != nil {
   450  			t.Fatalf("unable to create CSV encumbered output: %v", err)
   451  		}
   452  
   453  		// As the transaction is p2sh it should be accepted into the
   454  		// mempool and found within the next generated block.
   455  		if _, err := r.Client.SendRawTransaction(tx, true); err != nil {
   456  			t.Fatalf("unable to broadcast tx: %v", err)
   457  		}
   458  		blocks, err := r.Client.Generate(1)
   459  		if err != nil {
   460  			t.Fatalf("unable to generate blocks: %v", err)
   461  		}
   462  		txid := tx.TxHash()
   463  		assertTxInBlock(r, t, blocks[0], &txid)
   464  
   465  		// Generate a custom transaction which spends the CSV output.
   466  		sequenceNum := blockchain.LockTimeToSequence(false, 10)
   467  		spendingTx, err := spendCSVOutput(redeemScript, testUTXO,
   468  			sequenceNum, sweepOutput, txVersion)
   469  		if err != nil {
   470  			t.Fatalf("unable to spend csv output: %v", err)
   471  		}
   472  
   473  		// This transaction should be rejected from the mempool since
   474  		// CSV validation is already mempool policy pre-fork.
   475  		_, err = r.Client.SendRawTransaction(spendingTx, true)
   476  		if err == nil {
   477  			t.Fatalf("transaction should have been rejected, but was " +
   478  				"instead accepted")
   479  		}
   480  
   481  		// However, this transaction should be accepted in a custom
   482  		// generated block as CSV validation for scripts within blocks
   483  		// shouldn't yet be active.
   484  		txns := []*btcutil.Tx{btcutil.NewTx(spendingTx)}
   485  		block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
   486  		if err != nil {
   487  			t.Fatalf("unable to submit block: %v", err)
   488  		}
   489  		txid = spendingTx.TxHash()
   490  		assertTxInBlock(r, t, block.Hash(), &txid)
   491  	}
   492  
   493  	// At this point, the block height should be 107: we started at height
   494  	// 101, then generated 2 blocks in each loop iteration above.
   495  	assertChainHeight(r, t, 107)
   496  
   497  	// With the height at 107 we need 200 blocks to be mined after the
   498  	// genesis target period, so we mine 192 blocks. This'll put us at
   499  	// height 299. The getblockchaininfo call checks the state for the
   500  	// block AFTER the current height.
   501  	numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 8
   502  	if _, err := r.Client.Generate(numBlocks); err != nil {
   503  		t.Fatalf("unable to generate blocks: %v", err)
   504  	}
   505  
   506  	assertChainHeight(r, t, 299)
   507  	assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive)
   508  
   509  	// Knowing the number of outputs needed for the tests below, create a
   510  	// fresh output for use within each of the test-cases below.
   511  	const relativeTimeLock = 512
   512  	const numTests = 8
   513  	type csvOutput struct {
   514  		RedeemScript []byte
   515  		Utxo         *wire.OutPoint
   516  		Timelock     int32
   517  	}
   518  	var spendableInputs [numTests]csvOutput
   519  
   520  	// Create three outputs which have a block-based sequence locks, and
   521  	// three outputs which use the above time based sequence lock.
   522  	for i := 0; i < numTests; i++ {
   523  		timeLock := relativeTimeLock
   524  		isSeconds := true
   525  		if i < 7 {
   526  			timeLock = relativeBlockLock
   527  			isSeconds = false
   528  		}
   529  
   530  		redeemScript, utxo, tx, err := createCSVOutput(r, t, outputAmt,
   531  			int32(timeLock), isSeconds)
   532  		if err != nil {
   533  			t.Fatalf("unable to create CSV output: %v", err)
   534  		}
   535  
   536  		if _, err := r.Client.SendRawTransaction(tx, true); err != nil {
   537  			t.Fatalf("unable to broadcast transaction: %v", err)
   538  		}
   539  
   540  		spendableInputs[i] = csvOutput{
   541  			RedeemScript: redeemScript,
   542  			Utxo:         utxo,
   543  			Timelock:     int32(timeLock),
   544  		}
   545  	}
   546  
   547  	// Mine a single block including all the transactions generated above.
   548  	if _, err := r.Client.Generate(1); err != nil {
   549  		t.Fatalf("unable to generate block: %v", err)
   550  	}
   551  
   552  	// Now mine 10 additional blocks giving the inputs generated above a
   553  	// age of 11. Space out each block 10 minutes after the previous block.
   554  	prevBlockHash, err := r.Client.GetBestBlockHash()
   555  	if err != nil {
   556  		t.Fatalf("unable to get prior block hash: %v", err)
   557  	}
   558  	prevBlock, err := r.Client.GetBlock(prevBlockHash)
   559  	if err != nil {
   560  		t.Fatalf("unable to get block: %v", err)
   561  	}
   562  	for i := 0; i < relativeBlockLock; i++ {
   563  		timeStamp := prevBlock.Header.Timestamp.Add(time.Minute * 10)
   564  		b, err := r.GenerateAndSubmitBlock(nil, -1, timeStamp)
   565  		if err != nil {
   566  			t.Fatalf("unable to generate block: %v", err)
   567  		}
   568  
   569  		prevBlock = b.MsgBlock()
   570  	}
   571  
   572  	// A helper function to create fully signed transactions in-line during
   573  	// the array initialization below.
   574  	var inputIndex uint32
   575  	makeTxCase := func(sequenceNum uint32, txVersion int32) *wire.MsgTx {
   576  		csvInput := spendableInputs[inputIndex]
   577  
   578  		tx, err := spendCSVOutput(csvInput.RedeemScript, csvInput.Utxo,
   579  			sequenceNum, sweepOutput, txVersion)
   580  		if err != nil {
   581  			t.Fatalf("unable to spend CSV output: %v", err)
   582  		}
   583  
   584  		inputIndex++
   585  		return tx
   586  	}
   587  
   588  	tests := [numTests]struct {
   589  		tx     *wire.MsgTx
   590  		accept bool
   591  	}{
   592  		// A valid transaction with a single input a sequence number
   593  		// creating a 100 block relative time-lock. This transaction
   594  		// should be rejected as its version number is 1, and only tx
   595  		// of version > 2 will trigger the CSV behavior.
   596  		{
   597  			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 100), 1),
   598  			accept: false,
   599  		},
   600  		// A transaction of version 2 spending a single input. The
   601  		// input has a relative time-lock of 1 block, but the disable
   602  		// bit it set. The transaction should be rejected as a result.
   603  		{
   604  			tx: makeTxCase(
   605  				blockchain.LockTimeToSequence(false, 1)|wire.SequenceLockTimeDisabled,
   606  				2,
   607  			),
   608  			accept: false,
   609  		},
   610  		// A v2 transaction with a single input having a 9 block
   611  		// relative time lock. The referenced input is 11 blocks old,
   612  		// but the CSV output requires a 10 block relative lock-time.
   613  		// Therefore, the transaction should be rejected.
   614  		{
   615  			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 9), 2),
   616  			accept: false,
   617  		},
   618  		// A v2 transaction with a single input having a 10 block
   619  		// relative time lock. The referenced input is 11 blocks old so
   620  		// the transaction should be accepted.
   621  		{
   622  			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 10), 2),
   623  			accept: true,
   624  		},
   625  		// A v2 transaction with a single input having a 11 block
   626  		// relative time lock. The input referenced has an input age of
   627  		// 11 and the CSV op-code requires 10 blocks to have passed, so
   628  		// this transaction should be accepted.
   629  		{
   630  			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 11), 2),
   631  			accept: true,
   632  		},
   633  		// A v2 transaction whose input has a 1000 blck relative time
   634  		// lock.  This should be rejected as the input's age is only 11
   635  		// blocks.
   636  		{
   637  			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 1000), 2),
   638  			accept: false,
   639  		},
   640  		// A v2 transaction with a single input having a 512,000 second
   641  		// relative time-lock. This transaction should be rejected as 6
   642  		// days worth of blocks haven't yet been mined. The referenced
   643  		// input doesn't have sufficient age.
   644  		{
   645  			tx:     makeTxCase(blockchain.LockTimeToSequence(true, 512000), 2),
   646  			accept: false,
   647  		},
   648  		// A v2 transaction whose single input has a 512 second
   649  		// relative time-lock. This transaction should be accepted as
   650  		// finalized.
   651  		{
   652  			tx:     makeTxCase(blockchain.LockTimeToSequence(true, 512), 2),
   653  			accept: true,
   654  		},
   655  	}
   656  
   657  	for i, test := range tests {
   658  		txid, err := r.Client.SendRawTransaction(test.tx, true)
   659  		switch {
   660  		// Test case passes, nothing further to report.
   661  		case test.accept && err == nil:
   662  
   663  		// Transaction should have been accepted but we have a non-nil
   664  		// error.
   665  		case test.accept && err != nil:
   666  			t.Fatalf("test #%d, transaction should be accepted, "+
   667  				"but was rejected: %v", i, err)
   668  
   669  		// Transaction should have been rejected, but it was accepted.
   670  		case !test.accept && err == nil:
   671  			t.Fatalf("test #%d, transaction should be rejected, "+
   672  				"but was accepted", i)
   673  
   674  		// Transaction was rejected as wanted, nothing more to do.
   675  		case !test.accept && err != nil:
   676  		}
   677  
   678  		// If the transaction should be rejected, manually mine a block
   679  		// with the non-final transaction. It should be rejected.
   680  		if !test.accept {
   681  			txns := []*btcutil.Tx{btcutil.NewTx(test.tx)}
   682  			_, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
   683  			if err == nil {
   684  				t.Fatalf("test #%d, invalid block accepted", i)
   685  			}
   686  
   687  			continue
   688  		}
   689  
   690  		// Generate a block, the transaction should be included within
   691  		// the newly mined block.
   692  		blockHashes, err := r.Client.Generate(1)
   693  		if err != nil {
   694  			t.Fatalf("unable to mine block: %v", err)
   695  		}
   696  		assertTxInBlock(r, t, blockHashes[0], txid)
   697  	}
   698  }