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