コード例 #1
0
ファイル: csv_fork_test.go プロジェクト: Roasbeef/btcd
// createCSVOutput creates an output paying to a trivially redeemable CSV
// pkScript with the specified time-lock.
func createCSVOutput(r *rpctest.Harness, t *testing.T,
	numSatoshis btcutil.Amount, timeLock int32,
	isSeconds bool) ([]byte, *wire.OutPoint, *wire.MsgTx, error) {

	// Convert the time-lock to the proper sequence lock based according to
	// if the lock is seconds or time based.
	sequenceLock := blockchain.LockTimeToSequence(isSeconds,
		uint32(timeLock))

	// Our CSV script is simply: <sequenceLock> OP_CSV OP_DROP
	b := txscript.NewScriptBuilder().
		AddInt64(int64(sequenceLock)).
		AddOp(txscript.OP_CHECKSEQUENCEVERIFY).
		AddOp(txscript.OP_DROP)
	csvScript, err := b.Script()
	if err != nil {
		return nil, nil, nil, err
	}

	// Using the script generated above, create a P2SH output which will be
	// accepted into the mempool.
	p2shAddr, err := btcutil.NewAddressScriptHash(csvScript, r.ActiveNet)
	if err != nil {
		return nil, nil, nil, err
	}
	p2shScript, err := txscript.PayToAddrScript(p2shAddr)
	if err != nil {
		return nil, nil, nil, err
	}
	output := &wire.TxOut{
		PkScript: p2shScript,
		Value:    int64(numSatoshis),
	}

	// Finally create a valid transaction which creates the output crafted
	// above.
	tx, err := r.CreateTransaction([]*wire.TxOut{output}, 10)
	if err != nil {
		return nil, nil, nil, err
	}

	var outputIndex uint32
	if !bytes.Equal(tx.TxOut[0].PkScript, p2shScript) {
		outputIndex = 1
	}

	utxo := &wire.OutPoint{
		Hash:  tx.TxHash(),
		Index: outputIndex,
	}

	return csvScript, utxo, tx, nil
}
コード例 #2
0
ファイル: chain_test.go プロジェクト: Roasbeef/btcd
// TestCalcSequenceLock tests the LockTimeToSequence function, and the
// CalcSequenceLock method of a Chain instance. The tests exercise several
// combinations of inputs to the CalcSequenceLock function in order to ensure
// the returned SequenceLocks are correct for each test instance.
func TestCalcSequenceLock(t *testing.T) {
	netParams := &chaincfg.SimNetParams

	// Create a new database and chain instance to run tests against.
	chain, teardownFunc, err := chainSetup("calcseqlock", netParams)
	if err != nil {
		t.Errorf("Failed to setup chain instance: %v", err)
		return
	}
	defer teardownFunc()

	// Since we're not dealing with the real block chain, disable
	// checkpoints and set the coinbase maturity to 1.
	chain.DisableCheckpoints(true)
	chain.TstSetCoinbaseMaturity(1)

	// Create a test mining address to use for the blocks we'll generate
	// shortly below.
	k := bytes.Repeat([]byte{1}, 32)
	_, miningPub := btcec.PrivKeyFromBytes(btcec.S256(), k)
	miningAddr, err := btcutil.NewAddressPubKey(miningPub.SerializeCompressed(),
		netParams)
	if err != nil {
		t.Fatalf("unable to generate mining addr: %v", err)
	}

	// We'll keep track of the previous block for back pointers in blocks
	// we generated, and also the generated blocks along with the MTP from
	// their PoV to aide with our relative time lock calculations.
	var prevBlock *btcutil.Block
	var blocksWithMTP []struct {
		block *btcutil.Block
		mtp   time.Time
	}

	// We need to activate CSV in order to test the processing logic, so
	// manually craft the block version that's used to signal the soft-fork
	// activation.
	csvBit := netParams.Deployments[chaincfg.DeploymentCSV].BitNumber
	blockVersion := int32(0x20000000 | (uint32(1) << csvBit))

	// Generate enough blocks to activate CSV, collecting each of the
	// blocks into a slice for later use.
	numBlocksToActivate := (netParams.MinerConfirmationWindow * 3)
	for i := uint32(0); i < numBlocksToActivate; i++ {
		block, err := rpctest.CreateBlock(prevBlock, nil, blockVersion,
			time.Time{}, miningAddr, netParams)
		if err != nil {
			t.Fatalf("unable to generate block: %v", err)
		}

		mtp := chain.BestSnapshot().MedianTime

		_, isOrphan, err := chain.ProcessBlock(block, blockchain.BFNone)
		if err != nil {
			t.Fatalf("ProcessBlock fail on block %v: %v\n", i, err)
		}
		if isOrphan {
			t.Fatalf("ProcessBlock incorrectly returned block %v "+
				"is an orphan\n", i)
		}

		blocksWithMTP = append(blocksWithMTP, struct {
			block *btcutil.Block
			mtp   time.Time
		}{
			block: block,
			mtp:   mtp,
		})

		prevBlock = block
	}

	// Create a utxo view with all the utxos within the blocks created
	// above.
	utxoView := blockchain.NewUtxoViewpoint()
	for blockHeight, blockWithMTP := range blocksWithMTP {
		for _, tx := range blockWithMTP.block.Transactions() {
			utxoView.AddTxOuts(tx, int32(blockHeight))
		}
	}
	utxoView.SetBestHash(blocksWithMTP[len(blocksWithMTP)-1].block.Hash())

	// The median time calculated from the PoV of the best block in our
	// test chain. For unconfirmed inputs, this value will be used since
	// the MTP will be calculated from the PoV of the yet-to-be-mined
	// block.
	nextMedianTime := int64(1401292712)

	// We'll refer to this utxo within each input in the transactions
	// created below. This utxo has an age of 4 blocks and was mined within
	// block 297
	targetTx := blocksWithMTP[len(blocksWithMTP)-4].block.Transactions()[0]
	utxo := wire.OutPoint{
		Hash:  *targetTx.Hash(),
		Index: 0,
	}

	// Obtain the median time past from the PoV of the input created above.
	// The MTP for the input is the MTP from the PoV of the block *prior*
	// to the one that included it.
	medianTime := blocksWithMTP[len(blocksWithMTP)-5].mtp.Unix()

	// Add an additional transaction which will serve as our unconfirmed
	// output.
	var fakeScript []byte
	unConfTx := &wire.MsgTx{
		TxOut: []*wire.TxOut{{
			PkScript: fakeScript,
			Value:    5,
		}},
	}
	unConfUtxo := wire.OutPoint{
		Hash:  unConfTx.TxHash(),
		Index: 0,
	}

	// Adding a utxo with a height of 0x7fffffff indicates that the output
	// is currently unmined.
	utxoView.AddTxOuts(btcutil.NewTx(unConfTx), 0x7fffffff)

	tests := []struct {
		tx      *btcutil.Tx
		view    *blockchain.UtxoViewpoint
		want    *blockchain.SequenceLock
		mempool bool
	}{
		// A transaction of version one should disable sequence locks
		// as the new sequence number semantics only apply to
		// transactions version 2 or higher.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 1,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 3),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     -1,
				BlockHeight: -1,
			},
		},
		// A transaction with a single input, that a max int sequence
		// number. This sequence number has the high bit set, so
		// sequence locks should be disabled.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         wire.MaxTxInSequenceNum,
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     -1,
				BlockHeight: -1,
			},
		},
		// A transaction with a single input whose lock time is
		// expressed in seconds. However, the specified lock time is
		// below the required floor for time based lock times since
		// they have time granularity of 512 seconds. As a result, the
		// seconds lock-time should be just before the median time of
		// the targeted block.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 2),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     medianTime - 1,
				BlockHeight: -1,
			},
		},
		// A transaction with a single input whose lock time is
		// expressed in seconds. The number of seconds should be 1023
		// seconds after the median past time of the last block in the
		// chain.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 1024),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     medianTime + 1023,
				BlockHeight: -1,
			},
		},
		// A transaction with multiple inputs. The first input has a
		// sequence lock in blocks with a value of 4. The last input
		// has a sequence number with a value of 5, but has the disable
		// bit set. So the first lock should be selected as it's the
		// target lock as its the furthest in the future lock that
		// isn't disabled.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 2560),
				}, {
					PreviousOutPoint: utxo,
					Sequence: blockchain.LockTimeToSequence(false, 5) |
						wire.SequenceLockTimeDisabled,
				}, {
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 4),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
				BlockHeight: 299,
			},
		},
		// Transaction has a single input spending the genesis block
		// transaction. The input's sequence number is encodes a
		// relative lock-time in blocks (3 blocks). The sequence lock
		// should have a value of -1 for seconds, but a block height of
		// 298 meaning it can be included at height 299.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 3),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     -1,
				BlockHeight: 298,
			},
		},
		// A transaction with two inputs with lock times expressed in
		// seconds. The selected sequence lock value for seconds should
		// be the time further in the future.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 5120),
				}, {
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 2560),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     medianTime + (10 << wire.SequenceLockTimeGranularity) - 1,
				BlockHeight: -1,
			},
		},
		// A transaction with two inputs with lock times expressed in
		// seconds. The selected sequence lock value for blocks should
		// be the height further in the future. The converted absolute
		// block height should be 302, meaning it can be included in
		// block 303.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 1),
				}, {
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 7),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     -1,
				BlockHeight: 302,
			},
		},
		// A transaction with multiple inputs. Two inputs are time
		// based, and the other two are input maturity based. The lock
		// lying further into the future for both inputs should be
		// chosen.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 2560),
				}, {
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(true, 6656),
				}, {
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 3),
				}, {
					PreviousOutPoint: utxo,
					Sequence:         blockchain.LockTimeToSequence(false, 9),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
				BlockHeight: 304,
			},
		},
		// A transaction with a single unconfirmed input. As the input
		// is confirmed, the height of the input should be interpreted
		// as the height of the *next* block. The current block height
		// is 300, so the lock time should be calculated using height
		// 301 as a base. A 2 block relative lock means the transaction
		// can be included after block 302, so in 303.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: unConfUtxo,
					Sequence:         blockchain.LockTimeToSequence(false, 2),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     -1,
				BlockHeight: 302,
			},
		},
		// A transaction with a single unconfirmed input. The input has
		// a time based lock, so the lock time should be based off the
		// MTP of the *next* block.
		{
			tx: btcutil.NewTx(&wire.MsgTx{
				Version: 2,
				TxIn: []*wire.TxIn{{
					PreviousOutPoint: unConfUtxo,
					Sequence:         blockchain.LockTimeToSequence(true, 1024),
				}},
			}),
			view: utxoView,
			want: &blockchain.SequenceLock{
				Seconds:     nextMedianTime + 1023,
				BlockHeight: -1,
			},
		},
	}

	t.Logf("Running %v SequenceLock tests", len(tests))
	for i, test := range tests {
		seqLock, err := chain.CalcSequenceLock(test.tx, test.view, test.mempool)
		if err != nil {
			t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err)
		}

		if seqLock.Seconds != test.want.Seconds {
			t.Fatalf("test #%d got %v seconds want %v seconds",
				i, seqLock.Seconds, test.want.Seconds)
		}
		if seqLock.BlockHeight != test.want.BlockHeight {
			t.Fatalf("test #%d got height of %v want height of %v ",
				i, seqLock.BlockHeight, test.want.BlockHeight)
		}
	}
}
コード例 #3
0
ファイル: csv_fork_test.go プロジェクト: Roasbeef/btcd
// TestBIP0068AndBIP0112Activation tests for the proper adherence to the BIP
// 112 and BIP 68 rule-set after the activation of the CSV-package soft-fork.
//
// Overview:
//  - Pre soft-fork:
//    - A transaction spending a CSV output validly should be rejected from the
//    mempool, but accepted in a valid generated block including the
//    transaction.
//  - Post soft-fork:
//    - See the cases exercised within the table driven tests towards the end
//    of this test.
func TestBIP0068AndBIP0112Activation(t *testing.T) {
	t.Parallel()

	// We'd like the test proper evaluation and validation of the BIP 68
	// (sequence locks) and BIP 112 rule-sets which add input-age based
	// relative lock times.

	btcdCfg := []string{"--rejectnonstd"}
	r, err := rpctest.New(&chaincfg.SimNetParams, nil, btcdCfg)
	if err != nil {
		t.Fatal("unable to create primary harness: ", err)
	}
	if err := r.SetUp(true, 1); err != nil {
		t.Fatalf("unable to setup test chain: %v", err)
	}
	defer r.TearDown()

	assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdStarted)

	harnessAddr, err := r.NewAddress()
	if err != nil {
		t.Fatalf("unable to obtain harness address: %v", err)
	}
	harnessScript, err := txscript.PayToAddrScript(harnessAddr)
	if err != nil {
		t.Fatalf("unable to generate pkScript: %v", err)
	}

	const (
		outputAmt         = btcutil.SatoshiPerBitcoin
		relativeBlockLock = 10
	)

	sweepOutput := &wire.TxOut{
		Value:    outputAmt - 5000,
		PkScript: harnessScript,
	}

	// As the soft-fork hasn't yet activated _any_ transaction version
	// which uses the CSV opcode should be accepted. Since at this point,
	// CSV doesn't actually exist, it's just a NOP.
	for txVersion := int32(0); txVersion < 3; txVersion++ {
		// Create a trivially spendable output with a CSV lock-time of
		// 10 relative blocks.
		redeemScript, testUTXO, tx, err := createCSVOutput(r, t, outputAmt,
			relativeBlockLock, false)
		if err != nil {
			t.Fatalf("unable to create CSV encumbered output: %v", err)
		}

		// As the transaction is p2sh it should be accepted into the
		// mempool and found within the next generated block.
		if _, err := r.Node.SendRawTransaction(tx, true); err != nil {
			t.Fatalf("unable to broadcast tx: %v", err)
		}
		blocks, err := r.Node.Generate(1)
		if err != nil {
			t.Fatalf("unable to generate blocks: %v", err)
		}
		txid := tx.TxHash()
		assertTxInBlock(r, t, blocks[0], &txid)

		// Generate a custom transaction which spends the CSV output.
		sequenceNum := blockchain.LockTimeToSequence(false, 10)
		spendingTx, err := spendCSVOutput(redeemScript, testUTXO,
			sequenceNum, sweepOutput, txVersion)
		if err != nil {
			t.Fatalf("unable to spend csv output: %v", err)
		}

		// This transaction should be rejected from the mempool since
		// CSV validation is already mempool policy pre-fork.
		_, err = r.Node.SendRawTransaction(spendingTx, true)
		if err == nil {
			t.Fatalf("transaction should have been rejected, but was " +
				"instead accepted")
		}

		// However, this transaction should be accepted in a custom
		// generated block as CSV validation for scripts within blocks
		// shouldn't yet be active.
		txns := []*btcutil.Tx{btcutil.NewTx(spendingTx)}
		block, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
		if err != nil {
			t.Fatalf("unable to submit block: %v", err)
		}
		txid = spendingTx.TxHash()
		assertTxInBlock(r, t, block.Hash(), &txid)
	}

	// At this point, the block height should be 107: we started at height
	// 101, then generated 2 blocks in each loop iteration above.
	assertChainHeight(r, t, 107)

	// With the height at 107 we need 200 blocks to be mined after the
	// genesis target period, so we mine 192 blocks. This'll put us at
	// height 299. The getblockchaininfo call checks the state for the
	// block AFTER the current height.
	numBlocks := (r.ActiveNet.MinerConfirmationWindow * 2) - 8
	if _, err := r.Node.Generate(numBlocks); err != nil {
		t.Fatalf("unable to generate blocks: %v", err)
	}

	assertChainHeight(r, t, 299)
	assertSoftForkStatus(r, t, csvKey, blockchain.ThresholdActive)

	// Knowing the number of outputs needed for the tests below, create a
	// fresh output for use within each of the test-cases below.
	const relativeTimeLock = 512
	const numTests = 8
	type csvOutput struct {
		RedeemScript []byte
		Utxo         *wire.OutPoint
		Timelock     int32
	}
	var spendableInputs [numTests]csvOutput

	// Create three outputs which have a block-based sequence locks, and
	// three outputs which use the above time based sequence lock.
	for i := 0; i < numTests; i++ {
		timeLock := relativeTimeLock
		isSeconds := true
		if i < 7 {
			timeLock = relativeBlockLock
			isSeconds = false
		}

		redeemScript, utxo, tx, err := createCSVOutput(r, t, outputAmt,
			int32(timeLock), isSeconds)
		if err != nil {
			t.Fatalf("unable to create CSV output: %v", err)
		}

		if _, err := r.Node.SendRawTransaction(tx, true); err != nil {
			t.Fatalf("unable to broadcast transaction: %v", err)
		}

		spendableInputs[i] = csvOutput{
			RedeemScript: redeemScript,
			Utxo:         utxo,
			Timelock:     int32(timeLock),
		}
	}

	// Mine a single block including all the transactions generated above.
	if _, err := r.Node.Generate(1); err != nil {
		t.Fatalf("unable to generate block: %v", err)
	}

	// Now mine 10 additional blocks giving the inputs generated above a
	// age of 11. Space out each block 10 minutes after the previous block.
	prevBlockHash, err := r.Node.GetBestBlockHash()
	if err != nil {
		t.Fatalf("unable to get prior block hash: %v", err)
	}
	prevBlock, err := r.Node.GetBlock(prevBlockHash)
	if err != nil {
		t.Fatalf("unable to get block: %v", err)
	}
	for i := 0; i < relativeBlockLock; i++ {
		timeStamp := prevBlock.Header.Timestamp.Add(time.Minute * 10)
		b, err := r.GenerateAndSubmitBlock(nil, -1, timeStamp)
		if err != nil {
			t.Fatalf("unable to generate block: %v", err)
		}

		prevBlock = b.MsgBlock()
	}

	// A helper function to create fully signed transactions in-line during
	// the array initialization below.
	var inputIndex uint32
	makeTxCase := func(sequenceNum uint32, txVersion int32) *wire.MsgTx {
		csvInput := spendableInputs[inputIndex]

		tx, err := spendCSVOutput(csvInput.RedeemScript, csvInput.Utxo,
			sequenceNum, sweepOutput, txVersion)
		if err != nil {
			t.Fatalf("unable to spend CSV output: %v", err)
		}

		inputIndex++
		return tx
	}

	tests := [numTests]struct {
		tx     *wire.MsgTx
		accept bool
	}{
		// A valid transaction with a single input a sequence number
		// creating a 100 block relative time-lock. This transaction
		// should be rejected as its version number is 1, and only tx
		// of version > 2 will trigger the CSV behavior.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 100), 1),
			accept: false,
		},
		// A transaction of version 2 spending a single input. The
		// input has a relative time-lock of 1 block, but the disable
		// bit it set. The transaction should be rejected as a result.
		{
			tx: makeTxCase(
				blockchain.LockTimeToSequence(false, 1)|wire.SequenceLockTimeDisabled,
				2,
			),
			accept: false,
		},
		// A v2 transaction with a single input having a 9 block
		// relative time lock. The referenced input is 11 blocks old,
		// but the CSV output requires a 10 block relative lock-time.
		// Therefore, the transaction should be rejected.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 9), 2),
			accept: false,
		},
		// A v2 transaction with a single input having a 10 block
		// relative time lock. The referenced input is 11 blocks old so
		// the transaction should be accepted.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 10), 2),
			accept: true,
		},
		// A v2 transaction with a single input having a 11 block
		// relative time lock. The input referenced has an input age of
		// 11 and the CSV op-code requires 10 blocks to have passed, so
		// this transaction should be accepted.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 11), 2),
			accept: true,
		},
		// A v2 transaction whose input has a 1000 blck relative time
		// lock.  This should be rejected as the input's age is only 11
		// blocks.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(false, 1000), 2),
			accept: false,
		},
		// A v2 transaction with a single input having a 512,000 second
		// relative time-lock. This transaction should be rejected as 6
		// days worth of blocks haven't yet been mined. The referenced
		// input doesn't have sufficient age.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(true, 512000), 2),
			accept: false,
		},
		// A v2 transaction whose single input has a 512 second
		// relative time-lock. This transaction should be accepted as
		// finalized.
		{
			tx:     makeTxCase(blockchain.LockTimeToSequence(true, 512), 2),
			accept: true,
		},
	}

	for i, test := range tests {
		txid, err := r.Node.SendRawTransaction(test.tx, true)
		switch {
		// Test case passes, nothing further to report.
		case test.accept && err == nil:

		// Transaction should have been accepted but we have a non-nil
		// error.
		case test.accept && err != nil:
			t.Fatalf("test #%d, transaction should be accepted, "+
				"but was rejected: %v", i, err)

		// Transaction should have been rejected, but it was accepted.
		case !test.accept && err == nil:
			t.Fatalf("test #%d, transaction should be rejected, "+
				"but was accepted", i)

		// Transaction was rejected as wanted, nothing more to do.
		case !test.accept && err != nil:
		}

		// If the transaction should be rejected, manually mine a block
		// with the non-final transaction. It should be rejected.
		if !test.accept {
			txns := []*btcutil.Tx{btcutil.NewTx(test.tx)}
			_, err := r.GenerateAndSubmitBlock(txns, -1, time.Time{})
			if err == nil {
				t.Fatalf("test #%d, invalid block accepted", i)
			}

			continue
		}

		// Generate a block, the transaction should be included within
		// the newly mined block.
		blockHashes, err := r.Node.Generate(1)
		if err != nil {
			t.Fatalf("unable to mine block: %v", err)
		}
		assertTxInBlock(r, t, blockHashes[0], txid)
	}
}