diff --git a/blockchain/chain_test.go b/blockchain/chain_test.go
index 629db0f39..fcdcaf5cf 100644
--- a/blockchain/chain_test.go
+++ b/blockchain/chain_test.go
@@ -1,19 +1,16 @@
-// Copyright (c) 2013-2016 The btcsuite developers
+// Copyright (c) 2013-2017 The btcsuite developers
 // Use of this source code is governed by an ISC
 // license that can be found in the LICENSE file.
 
 package blockchain_test
 
 import (
-	"bytes"
 	"testing"
 	"time"
 
 	"github.com/btcsuite/btcd/blockchain"
-	"github.com/btcsuite/btcd/btcec"
 	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
-	"github.com/btcsuite/btcd/integration/rpctest"
 	"github.com/btcsuite/btcd/wire"
 	"github.com/btcsuite/btcutil"
 )
@@ -121,116 +118,64 @@ func TestHaveBlock(t *testing.T) {
 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, set the coinbase
-	// maturity to 1.
-	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.
+	// Generate enough synthetic blocks to activate CSV.
+	chain, node := blockchain.TstNewFakeChain(netParams)
+	blockTime := node.Header().Timestamp
 	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
+		blockTime = blockTime.Add(time.Second)
+		node = chain.TstNewFakeNode(node, blockVersion, 0, blockTime)
 	}
 
-	// Create a utxo view with all the utxos within the blocks created
-	// above.
+	// Create a utxo view with a fake utxo for the inputs used in the
+	// transactions created below.  This utxo is added such that it has an
+	// age of 4 blocks.
+	targetTx := btcutil.NewTx(&wire.MsgTx{
+		TxOut: []*wire.TxOut{{
+			PkScript: nil,
+			Value:    10,
+		}},
+	})
 	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())
+	utxoView.AddTxOuts(targetTx, int32(numBlocksToActivate)-4)
+	bestHeader := node.Header()
+	bestHash := bestHeader.BlockHash()
+	utxoView.SetBestHash(&bestHash)
 
-	// We'll refer to this utxo within each input in the transactions
-	// created below. This utxo has an age of 4 blocks.  Note that the
-	// sequence lock heights are always calculated from the same point of
-	// view that they were originally calculated from for a given utxo.
-	// That is to say, the height prior to it.
-	targetBlock := blocksWithMTP[len(blocksWithMTP)-4].block
-	targetTx := targetBlock.Transactions()[0]
+	// Create a utxo that spends the fake utxo created above for use in the
+	// transactions created in the tests.  It has an age of 4 blocks.  Note
+	// that the sequence lock heights are always calculated from the same
+	// point of view that they were originally calculated from for a given
+	// utxo.  That is to say, the height prior to it.
 	utxo := wire.OutPoint{
 		Hash:  *targetTx.Hash(),
 		Index: 0,
 	}
-	prevUtxoHeight := targetBlock.Height() - 1
+	prevUtxoHeight := int32(numBlocksToActivate) - 4
 
 	// 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()
+	medianTime := node.RelativeAncestor(5).CalcPastMedianTime().Unix()
 
-	// The median time calculated from the PoV of the best block in our
+	// The median time calculated from the PoV of the best block in the
 	// 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 := blocksWithMTP[len(blocksWithMTP)-1].mtp.Unix() + 1
-	nextBlockHeight := blocksWithMTP[len(blocksWithMTP)-1].block.Height() + 1
+	nextMedianTime := node.CalcPastMedianTime().Unix()
+	nextBlockHeight := int32(numBlocksToActivate) + 1
 
 	// Add an additional transaction which will serve as our unconfirmed
 	// output.
-	var fakeScript []byte
 	unConfTx := &wire.MsgTx{
 		TxOut: []*wire.TxOut{{
-			PkScript: fakeScript,
+			PkScript: nil,
 			Value:    5,
 		}},
 	}
@@ -244,7 +189,7 @@ func TestCalcSequenceLock(t *testing.T) {
 	utxoView.AddTxOuts(btcutil.NewTx(unConfTx), 0x7fffffff)
 
 	tests := []struct {
-		tx      *btcutil.Tx
+		tx      *wire.MsgTx
 		view    *blockchain.UtxoViewpoint
 		mempool bool
 		want    *blockchain.SequenceLock
@@ -253,13 +198,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// as the new sequence number semantics only apply to
 		// transactions version 2 or higher.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 1,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(false, 3),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     -1,
@@ -270,13 +215,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// This sequence number has the high bit set, so sequence locks
 		// should be disabled.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
 					Sequence:         wire.MaxTxInSequenceNum,
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     -1,
@@ -290,13 +235,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// seconds lock-time should be just before the median time of
 		// the targeted block.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(true, 2),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     medianTime - 1,
@@ -308,13 +253,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// seconds after the median past time of the last block in the
 		// chain.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(true, 1024),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     medianTime + 1023,
@@ -328,7 +273,7 @@ func TestCalcSequenceLock(t *testing.T) {
 		// bit set.  So the first lock should be selected as it's the
 		// latest lock that isn't disabled.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
@@ -341,7 +286,7 @@ func TestCalcSequenceLock(t *testing.T) {
 					Sequence: blockchain.LockTimeToSequence(false, 5) |
 						wire.SequenceLockTimeDisabled,
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     medianTime + (5 << wire.SequenceLockTimeGranularity) - 1,
@@ -353,13 +298,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// sequence lock should  have a value of -1 for seconds, but a
 		// height of 2 meaning it can be included at height 3.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(false, 3),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     -1,
@@ -370,7 +315,7 @@ func TestCalcSequenceLock(t *testing.T) {
 		// seconds.  The selected sequence lock value for seconds should
 		// be the time further in the future.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
@@ -379,7 +324,7 @@ func TestCalcSequenceLock(t *testing.T) {
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(true, 2560),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     medianTime + (10 << wire.SequenceLockTimeGranularity) - 1,
@@ -391,7 +336,7 @@ func TestCalcSequenceLock(t *testing.T) {
 		// be the height further in the future, so a height of 10
 		// indicating it can be included at height 11.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
@@ -400,7 +345,7 @@ func TestCalcSequenceLock(t *testing.T) {
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(false, 11),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     -1,
@@ -411,7 +356,7 @@ func TestCalcSequenceLock(t *testing.T) {
 		// based, and the other two are block based. The lock lying
 		// further into the future for both inputs should be chosen.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: utxo,
@@ -426,7 +371,7 @@ func TestCalcSequenceLock(t *testing.T) {
 					PreviousOutPoint: utxo,
 					Sequence:         blockchain.LockTimeToSequence(false, 9),
 				}},
-			}),
+			},
 			view: utxoView,
 			want: &blockchain.SequenceLock{
 				Seconds:     medianTime + (13 << wire.SequenceLockTimeGranularity) - 1,
@@ -440,13 +385,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// *next* block height, indicating it can be included 2 blocks
 		// after that.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: unConfUtxo,
 					Sequence:         blockchain.LockTimeToSequence(false, 2),
 				}},
-			}),
+			},
 			view:    utxoView,
 			mempool: true,
 			want: &blockchain.SequenceLock{
@@ -458,13 +403,13 @@ func TestCalcSequenceLock(t *testing.T) {
 		// a time based lock, so the lock time should be based off the
 		// MTP of the *next* block.
 		{
-			tx: btcutil.NewTx(&wire.MsgTx{
+			tx: &wire.MsgTx{
 				Version: 2,
 				TxIn: []*wire.TxIn{{
 					PreviousOutPoint: unConfUtxo,
 					Sequence:         blockchain.LockTimeToSequence(true, 1024),
 				}},
-			}),
+			},
 			view:    utxoView,
 			mempool: true,
 			want: &blockchain.SequenceLock{
@@ -476,7 +421,8 @@ func TestCalcSequenceLock(t *testing.T) {
 
 	t.Logf("Running %v SequenceLock tests", len(tests))
 	for i, test := range tests {
-		seqLock, err := chain.CalcSequenceLock(test.tx, test.view, test.mempool)
+		utilTx := btcutil.NewTx(test.tx)
+		seqLock, err := chain.CalcSequenceLock(utilTx, test.view, test.mempool)
 		if err != nil {
 			t.Fatalf("test #%d, unable to calc sequence lock: %v", i, err)
 		}
diff --git a/blockchain/internal_test.go b/blockchain/internal_test.go
index e7432adbb..15886b837 100644
--- a/blockchain/internal_test.go
+++ b/blockchain/internal_test.go
@@ -14,6 +14,10 @@ package blockchain
 
 import (
 	"sort"
+	"time"
+
+	"github.com/btcsuite/btcd/chaincfg"
+	"github.com/btcsuite/btcd/wire"
 )
 
 // TstSetCoinbaseMaturity makes the ability to set the coinbase maturity
@@ -45,3 +49,51 @@ var TstCheckBlockScripts = checkBlockScripts
 // TstDeserializeUtxoEntry makes the internal deserializeUtxoEntry function
 // available to the test package.
 var TstDeserializeUtxoEntry = deserializeUtxoEntry
+
+// TstNewFakeChain returns a chain that is usable for syntetic tests.  It is
+// important to note that this chain has no database associated with it, so
+// it is not usable with all functions and the tests must take care when making
+// use of it.
+func TstNewFakeChain(params *chaincfg.Params) (*BlockChain, *blockNode) {
+	// Create a genesis block node and block index index populated with it
+	// for use when creating the fake chain below.
+	node := newBlockNode(&params.GenesisBlock.Header, 0)
+	node.inMainChain = true
+	index := newBlockIndex(nil, params)
+	index.AddNode(node)
+
+	targetTimespan := int64(params.TargetTimespan / time.Second)
+	targetTimePerBlock := int64(params.TargetTimePerBlock / time.Second)
+	adjustmentFactor := params.RetargetAdjustmentFactor
+	return &BlockChain{
+		chainParams:         params,
+		timeSource:          NewMedianTime(),
+		minRetargetTimespan: targetTimespan / adjustmentFactor,
+		maxRetargetTimespan: targetTimespan * adjustmentFactor,
+		blocksPerRetarget:   int32(targetTimespan / targetTimePerBlock),
+		index:               index,
+		warningCaches:       newThresholdCaches(vbNumBits),
+		deploymentCaches:    newThresholdCaches(chaincfg.DefinedDeployments),
+		bestNode:            node,
+	}, node
+}
+
+// TstNewFakeNode creates a block node connected to the passed parent with the
+// provided fields populated and fake values for the other fields and adds it
+// to the blockchain's index as well as makes it the best node.
+func (b *BlockChain) TstNewFakeNode(parent *blockNode, blockVersion int32, bits uint32, timestamp time.Time) *blockNode {
+	// Make up a header and create a block node from it.
+	header := &wire.BlockHeader{
+		Version:   blockVersion,
+		PrevBlock: parent.hash,
+		Bits:      bits,
+		Timestamp: timestamp,
+	}
+	node := newBlockNode(header, parent.height+1)
+	node.parent = parent
+	node.workSum.Add(parent.workSum, node.workSum)
+
+	b.index.AddNode(node)
+	b.bestNode = node
+	return node
+}