Repository: decred/dcrd
Pull request #2534 (2020-12-28 14:41:39)

blockchain: Remove legacy sequence lock view. 
**This requires https://github.com/decred/dcrd/pull/2533.**

This removes the legacy sequence lock view and associated tests now that DCP0004 is activated and the activation height is buried sufficiently deep in the chain history.

It also removes the `IsFixSeqLocksAgendaActive` function and associated tests and removes related logic from `TestFixedSequenceLocks` that is no longer needed. 

Tested a full sync with checkpoints disabled on mainnet and testnet, and it worked as expected. 

davecgh: I accidentally included some review items from the PR this is based on.  I copied them over to the correct PR and cleaned them up from this one to prevent future confusion.
rstaudt2: Rebased. 

Patch:
diff --git a/blockchain/chaingen/generator.go b/blockchain/chaingen/generator.go
index 1a40cc70..9f6d6a67 100644
--- a/blockchain/chaingen/generator.go
+++ b/blockchain/chaingen/generator.go
@@ -1683,6 +1683,21 @@ func (g *Generator) ReplaceVoteBitsN(voteNum int, voteBits uint16) func(*wire.Ms
 	}
 }
 
+// ReplaceVoteBits returns a function that itself takes a block and modifies it
+// by replacing the vote bits of the stake transactions.
+//
+// NOTE: This must only be used as a munger to the 'NextBlock' function or it
+// will lead to an invalid live ticket pool.
+func (g *Generator) ReplaceVoteBits(voteBits uint16) func(*wire.MsgBlock) {
+	return func(b *wire.MsgBlock) {
+		for stxIdx, stx := range b.STransactions {
+			if isVoteTx(stx) {
+				g.ReplaceVoteBitsN(stxIdx, voteBits)(b)
+			}
+		}
+	}
+}
+
 // ReplaceBlockVersion returns a function that itself takes a block and modifies
 // it by replacing the stake version of the header.
 func ReplaceBlockVersion(newVersion int32) func(*wire.MsgBlock) {
diff --git a/blockchain/indexers/cfindex.go b/blockchain/indexers/cfindex.go
deleted file mode 100644
index b00e1e2f..00000000
--- a/blockchain/indexers/cfindex.go
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright (c) 2017 The btcsuite developers
-// Copyright (c) 2018-2020 The Decred developers
-// Use of this source code is governed by an ISC
-// license that can be found in the LICENSE file.
-
-package indexers
-
-import (
-	"context"
-	"errors"
-	"fmt"
-
-	"github.com/decred/dcrd/chaincfg/chainhash"
-	"github.com/decred/dcrd/chaincfg/v3"
-	"github.com/decred/dcrd/database/v2"
-	"github.com/decred/dcrd/dcrutil/v4"
-	"github.com/decred/dcrd/gcs/v3"
-	"github.com/decred/dcrd/gcs/v3/blockcf"
-	"github.com/decred/dcrd/wire"
-)
-
-const (
-	// cfIndexName is the human-readable name for the index.
-	cfIndexName = "committed filter index"
-
-	// cfIndexVersion is the current version of the committed filter index.
-	cfIndexVersion = 2
-)
-
-// Committed filters come in two flavors: basic and extended. They are
-// generated and dropped in pairs, and both are indexed by a block's hash.
-// Besides holding different content, they also live in different buckets.
-var (
-	// cfIndexParentBucketKey is the name of the parent bucket used to house
-	// the index. The rest of the buckets live below this bucket.
-	cfIndexParentBucketKey = []byte("cfindexparentbucket")
-
-	// cfIndexKeys is an array of db bucket names used to house indexes of
-	// block hashes to cfilters.
-	cfIndexKeys = [][]byte{
-		[]byte("cf0byhashidx"),
-		[]byte("cf1byhashidx"),
-	}
-
-	// cfHeaderKeys is an array of db bucket names used to house indexes of
-	// block hashes to cf headers.
-	cfHeaderKeys = [][]byte{
-		[]byte("cf0headerbyhashidx"),
-		[]byte("cf1headerbyhashidx"),
-	}
-
-	maxFilterType = uint8(len(cfHeaderKeys) - 1)
-)
-
-// dbFetchFilter retrieves a block's basic or extended filter. A filter's
-// absence is not considered an error.
-func dbFetchFilter(dbTx database.Tx, key []byte, h *chainhash.Hash) []byte {
-	idx := dbTx.Metadata().Bucket(cfIndexParentBucketKey).Bucket(key)
-	return idx.Get(h[:])
-}
-
-// dbFetchFilterHeader retrieves a block's basic or extended filter header.
-// A filter's absence is not considered an error.
-func dbFetchFilterHeader(dbTx database.Tx, key []byte, h *chainhash.Hash) ([]byte, error) {
-	idx := dbTx.Metadata().Bucket(cfIndexParentBucketKey).Bucket(key)
-
-	fh := idx.Get(h[:])
-	if fh == nil {
-		return make([]byte, chainhash.HashSize), nil
-	}
-	if len(fh) != chainhash.HashSize {
-		return nil, fmt.Errorf("invalid filter header length %v", len(fh))
-	}
-
-	return fh, nil
-}
-
-// dbStoreFilter stores a block's basic or extended filter.
-func dbStoreFilter(dbTx database.Tx, key []byte, h *chainhash.Hash, f []byte) error {
-	idx := dbTx.Metadata().Bucket(cfIndexParentBucketKey).Bucket(key)
-	return idx.Put(h[:], f)
-}
-
-// dbStoreFilterHeader stores a block's basic or extended filter header.
-func dbStoreFilterHeader(dbTx database.Tx, key []byte, h *chainhash.Hash, fh []byte) error {
-	if len(fh) != chainhash.HashSize {
-		return fmt.Errorf("invalid filter header length %v", len(fh))
-	}
-	idx := dbTx.Metadata().Bucket(cfIndexParentBucketKey).Bucket(key)
-	return idx.Put(h[:], fh)
-}
-
-// dbDeleteFilter deletes a filter's basic or extended filter.
-func dbDeleteFilter(dbTx database.Tx, key []byte, h *chainhash.Hash) error {
-	idx := dbTx.Metadata().Bucket(cfIndexParentBucketKey).Bucket(key)
-	return idx.Delete(h[:])
-}
-
-// dbDeleteFilterHeader deletes a filter's basic or extended filter header.
-func dbDeleteFilterHeader(dbTx database.Tx, key []byte, h *chainhash.Hash) error {
-	idx := dbTx.Metadata().Bucket(cfIndexParentBucketKey).Bucket(key)
-	return idx.Delete(h[:])
-}
-
-// CFIndex implements a committed filter (cf) by hash index.
-type CFIndex struct {
-	db          database.DB
-	chainParams *chaincfg.Params
-}
-
-// Ensure the CFIndex type implements the Indexer interface.
-var _ Indexer = (*CFIndex)(nil)
-
-// Init initializes the hash-based cf index. This is part of the Indexer
-// interface.
-func (idx *CFIndex) Init() error {
-	return nil // Nothing to do.
-}
-
-// Key returns the database key to use for the index as a byte slice. This is
-// part of the Indexer interface.
-func (idx *CFIndex) Key() []byte {
-	return cfIndexParentBucketKey
-}
-
-// Name returns the human-readable name of the index. This is part of the
-// Indexer interface.
-func (idx *CFIndex) Name() string {
-	return cfIndexName
-}
-
-// Version returns the current version of the index.
-//
-// This is part of the Indexer interface.
-func (idx *CFIndex) Version() uint32 {
-	return cfIndexVersion
-}
-
-// Create is invoked when the indexer manager determines the index needs to
-// be created for the first time. It creates buckets for the two hash-based cf
-// indexes (simple, extended).
-func (idx *CFIndex) Create(dbTx database.Tx) error {
-	meta := dbTx.Metadata()
-
-	cfIndexParentBucket, err := meta.CreateBucket(cfIndexParentBucketKey)
-	if err != nil {
-		return err
-	}
-
-	for _, bucketName := range cfIndexKeys {
-		_, err = cfIndexParentBucket.CreateBucket(bucketName)
-		if err != nil {
-			return err
-		}
-	}
-
-	for _, bucketName := range cfHeaderKeys {
-		_, err = cfIndexParentBucket.CreateBucket(bucketName)
-		if err != nil {
-			return err
-		}
-	}
-
-	firstHeader := make([]byte, chainhash.HashSize)
-	err = dbStoreFilterHeader(dbTx, cfHeaderKeys[wire.GCSFilterRegular],
-		&idx.chainParams.GenesisBlock.Header.PrevBlock, firstHeader)
-	if err != nil {
-		return err
-	}
-
-	return dbStoreFilterHeader(dbTx, cfHeaderKeys[wire.GCSFilterExtended],
-		&idx.chainParams.GenesisBlock.Header.PrevBlock, firstHeader)
-}
-
-// storeFilter stores a given filter, and performs the steps needed to
-// generate the filter's header.
-func storeFilter(dbTx database.Tx, block *dcrutil.Block, f *gcs.FilterV1, filterType wire.FilterType) error {
-	if uint8(filterType) > maxFilterType {
-		return errors.New("unsupported filter type")
-	}
-
-	// Figure out which buckets to use.
-	fkey := cfIndexKeys[filterType]
-	hkey := cfHeaderKeys[filterType]
-
-	// Start by storing the filter.
-	h := block.Hash()
-	var basicFilterBytes []byte
-	if f != nil {
-		basicFilterBytes = f.Bytes()
-	}
-	err := dbStoreFilter(dbTx, fkey, h, basicFilterBytes)
-	if err != nil {
-		return err
-	}
-
-	// Then fetch the previous block's filter header.
-	ph := &block.MsgBlock().Header.PrevBlock
-	pfh, err := dbFetchFilterHeader(dbTx, hkey, ph)
-	if err != nil {
-		return err
-	}
-
-	// Construct the new block's filter header, and store it.
-	prevHeader, err := chainhash.NewHash(pfh)
-	if err != nil {
-		return err
-	}
-	fh := gcs.MakeHeaderForFilter(f, prevHeader)
-	return dbStoreFilterHeader(dbTx, hkey, h, fh[:])
-}
-
-// ConnectBlock is invoked by the index manager when a new block has been
-// connected to the main chain. This indexer adds a hash-to-cf mapping for
-// every passed block. This is part of the Indexer interface.
-func (idx *CFIndex) ConnectBlock(dbTx database.Tx, block, parent *dcrutil.Block, _ PrevScripter, isTreasuryEnabled bool) error {
-	f, err := blockcf.Regular(block.MsgBlock())
-	if err != nil {
-		return err
-	}
-
-	err = storeFilter(dbTx, block, f, wire.GCSFilterRegular)
-	if err != nil {
-		return err
-	}
-
-	f, err = blockcf.Extended(block.MsgBlock())
-	if err != nil {
-		return err
-	}
-
-	return storeFilter(dbTx, block, f, wire.GCSFilterExtended)
-}
-
-// DisconnectBlock is invoked by the index manager when a block has been
-// disconnected from the main chain.  This indexer removes the hash-to-cf
-// mapping for every passed block. This is part of the Indexer interface.
-func (idx *CFIndex) DisconnectBlock(dbTx database.Tx, block, parent *dcrutil.Block, _ PrevScripter, isTreasuryEnabled bool) error {
-	for _, key := range cfIndexKeys {
-		err := dbDeleteFilter(dbTx, key, block.Hash())
-		if err != nil {
-			return err
-		}
-	}
-
-	for _, key := range cfHeaderKeys {
-		err := dbDeleteFilterHeader(dbTx, key, block.Hash())
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// FilterByBlockHash returns the serialized contents of a block's basic or
-// extended committed filter.
-//
-// Deprecated: This will be removed in the next major version.  Use
-// blockchain.FilterByBlockHash instead.
-func (idx *CFIndex) FilterByBlockHash(h *chainhash.Hash, filterType wire.FilterType) ([]byte, error) {
-	if uint8(filterType) > maxFilterType {
-		return nil, errors.New("unsupported filter type")
-	}
-
-	var f []byte
-	err := idx.db.View(func(dbTx database.Tx) error {
-		f = dbFetchFilter(dbTx, cfIndexKeys[filterType], h)
-		return nil
-	})
-	return f, err
-}
-
-// FilterHeaderByBlockHash returns the serialized contents of a block's basic
-// or extended committed filter header.
-//
-// Deprecated: This will be removed in the next major version.  Use
-// blockchain.FilterByBlockHash instead.
-func (idx *CFIndex) FilterHeaderByBlockHash(h *chainhash.Hash, filterType wire.FilterType) ([]byte, error) {
-	if uint8(filterType) > maxFilterType {
-		return nil, errors.New("unsupported filter type")
-	}
-
-	var fh []byte
-	err := idx.db.View(func(dbTx database.Tx) error {
-		var err error
-		fh, err = dbFetchFilterHeader(dbTx,
-			cfHeaderKeys[filterType], h)
-		return err
-	})
-	return fh, err
-}
-
-// NewCfIndex returns a new instance of an indexer that is used to create a
-// mapping of the hashes of all blocks in the blockchain to their respective
-// committed filters.
-//
-// It implements the Indexer interface which plugs into the IndexManager that
-// in turn is used by the blockchain package. This allows the index to be
-// seamlessly maintained along with the chain.
-func NewCfIndex(db database.DB, chainParams *chaincfg.Params) *CFIndex {
-	return &CFIndex{db: db, chainParams: chainParams}
-}
-
-// DropCfIndex drops the CF index from the provided database if exists.
-func DropCfIndex(ctx context.Context, db database.DB) error {
-	return dropIndexMetadata(db, cfIndexParentBucketKey, cfIndexName)
-}
diff --git a/blockchain/indexers/dropcfindex.go b/blockchain/indexers/dropcfindex.go
new file mode 100644
index 00000000..f1a35647
--- /dev/null
+++ b/blockchain/indexers/dropcfindex.go
@@ -0,0 +1,48 @@
+// Copyright (c) 2017 The btcsuite developers
+// Copyright (c) 2018-2020 The Decred developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package indexers
+
+import (
+	"context"
+
+	"github.com/decred/dcrd/database/v2"
+)
+
+const (
+	// cfIndexName is the human-readable name for the index.
+	cfIndexName = "committed filter index"
+)
+
+var (
+	// cfIndexParentBucketKey is the name of the parent bucket used to house
+	// the index. The rest of the buckets live below this bucket.
+	cfIndexParentBucketKey = []byte("cfindexparentbucket")
+)
+
+// DropCfIndex drops the CF index from the provided database if it exists.
+func DropCfIndex(ctx context.Context, db database.DB) error {
+	// Nothing to do if the index doesn't already exist.
+	exists, err := existsIndex(db, cfIndexParentBucketKey, cfIndexName)
+	if err != nil {
+		return err
+	}
+	if !exists {
+		return nil
+	}
+
+	log.Infof("Dropping all legacy %s entries.  This might take a while...",
+		cfIndexName)
+
+	// Remove the index tip, version, bucket, and in-progress drop flag now
+	// that all index entries have been removed.
+	err = dropIndexMetadata(db, cfIndexParentBucketKey, cfIndexName)
+	if err != nil {
+		return err
+	}
+
+	log.Infof("Dropped %s", cfIndexName)
+	return nil
+}
diff --git a/blockchain/thresholdstate.go b/blockchain/thresholdstate.go
index 5c3b1cda..90ecd0cc 100644
--- a/blockchain/thresholdstate.go
+++ b/blockchain/thresholdstate.go
@@ -641,49 +641,6 @@ func (b *BlockChain) IsLNFeaturesAgendaActive(prevHash *chainhash.Hash) (bool, e
 	return isActive, err
 }
 
-// isFixSeqLocksAgendaActive returns whether or not the fix sequence locks
-// agenda vote, as defined in DCP0004 has passed and is now active from the
-// point of view of the passed block node.
-//
-// It is important to note that, as the variable name indicates, this function
-// expects the block node prior to the block for which the deployment state is
-// desired.  In other words, the returned deployment state is for the block
-// AFTER the passed node.
-//
-// This function MUST be called with the chain state lock held (for writes).
-func (b *BlockChain) isFixSeqLocksAgendaActive(prevNode *blockNode) (bool, error) {
-	// Determine the correct deployment version for the fix sequence locks
-	// consensus vote as defined in DCP0004 or treat it as active when voting
-	// is not enabled for the current network.
-	const deploymentID = chaincfg.VoteIDFixLNSeqLocks
-	deploymentVer, ok := b.deploymentVers[deploymentID]
-	if !ok {
-		return true, nil
-	}
-
-	state, err := b.deploymentState(prevNode, deploymentVer, deploymentID)
-	if err != nil {
-		return false, err
-	}
-
-	// NOTE: The choice field of the return threshold state is not examined
-	// here because there is only one possible choice that can be active for
-	// the agenda, which is yes, so there is no need to check it.
-	return state.State == ThresholdActive, nil
-}
-
-// IsFixSeqLocksAgendaActive returns whether or not the fix sequence locks
-// agenda vote, as defined in DCP0004 has passed and is now active for the block
-// AFTER the current best chain block.
-//
-// This function is safe for concurrent access.
-func (b *BlockChain) IsFixSeqLocksAgendaActive() (bool, error) {
-	b.chainLock.Lock()
-	isActive, err := b.isFixSeqLocksAgendaActive(b.bestChain.Tip())
-	b.chainLock.Unlock()
-	return isActive, err
-}
-
 // isHeaderCommitmentsAgendaActive returns whether or not the header commitments
 // agenda vote, as defined in DCP0005 has passed and is now active from the
 // point of view of the passed block node.
diff --git a/blockchain/validate.go b/blockchain/validate.go
index fa979c24..e95ed048 100644
--- a/blockchain/validate.go
+++ b/blockchain/validate.go
@@ -2944,70 +2944,6 @@ func CountP2SHSigOps(tx *dcrutil.Tx, isCoinBaseTx bool, isStakeBaseTx bool, view
 	return totalSigOps, nil
 }
 
-// createLegacySeqLockView returns a view to use when calculating sequence locks
-// for the transactions in the regular tree that preserves the same incorrect
-// semantics that were present in previous versions of the software.
-func (b *BlockChain) createLegacySeqLockView(prevNode *blockNode, block, parent *dcrutil.Block, view *UtxoViewpoint) (*UtxoViewpoint, error) {
-	// Determine if the treasury agenda is active as of the block the view is
-	// being created for.
-	isTreasuryEnabled, err := view.blockChain.isTreasuryAgendaActive(prevNode)
-	if err != nil {
-		return nil, err
-	}
-
-	// Clone the real view to avoid mutating it.
-	seqLockView := view.clone()
-
-	// Ensure all of the inputs referenced by the transactions in the regular
-	// tree of the parent block and the parent block outputs are available in
-	// the legacy view so long as it has not been disapproved.
-	if headerApprovesParent(&block.MsgBlock().Header) {
-		err := seqLockView.fetchRegularInputUtxos(b.db, parent,
-			isTreasuryEnabled)
-		if err != nil {
-			return nil, err
-		}
-
-		for txInIdx, tx := range parent.Transactions() {
-			seqLockView.AddTxOuts(tx, parent.Height(),
-				uint32(txInIdx), isTreasuryEnabled)
-		}
-	}
-
-	// Ensure all of the inputs referenced by the transactions in the stake tree
-	// of the current block are available in the legacy view.
-	filteredSet := make(viewFilteredSet)
-	for _, stx := range block.STransactions() {
-		isVote := stake.IsSSGen(stx.MsgTx(), isTreasuryEnabled)
-		for txInIdx, txIn := range stx.MsgTx().TxIn {
-			// Ignore stakebase since it has no input.
-			if txInIdx == 0 && isVote {
-				continue
-			}
-
-			// Only request entries that are not already in the view from the
-			// database.
-			originHash := &txIn.PreviousOutPoint.Hash
-			filteredSet.add(seqLockView, originHash)
-		}
-	}
-	err = seqLockView.fetchUtxosMain(b.db, filteredSet)
-	if err != nil {
-		return nil, err
-	}
-
-	// Connect all of the transactions in the stake tree of the current block.
-	for txIdx, stx := range block.STransactions() {
-		err := seqLockView.connectTransaction(stx, block.Height(),
-			uint32(txIdx), nil, isTreasuryEnabled)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return seqLockView, nil
-}
-
 // checkNumSigOps Checks the number of P2SH signature operations to make
 // sure they don't overflow the limits.  It takes a cumulative number of sig
 // ops as an argument and increments will each call.
@@ -3590,27 +3526,6 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B
 		}
 	}
 
-	// Create a view which preserves the expected consensus semantics for
-	// relative lock times via sequence numbers once the stake vote for the
-	// agenda is active.
-	legacySeqLockView := view
-	lnFeaturesActive, err := b.isLNFeaturesAgendaActive(node.parent)
-	if err != nil {
-		return err
-	}
-	fixSeqLocksActive, err := b.isFixSeqLocksAgendaActive(node.parent)
-	if err != nil {
-		return err
-	}
-	if lnFeaturesActive && !fixSeqLocksActive {
-		var err error
-		legacySeqLockView, err = b.createLegacySeqLockView(node.parent, block,
-			parent, view)
-		if err != nil {
-			return err
-		}
-	}
-
 	// Disconnect all of the transactions in the regular transaction tree of
 	// the parent if the block being checked votes against it.
 	if node.height > 1 && !voteBitsApproveParent(node.voteBits) {
@@ -3657,6 +3572,10 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B
 	// Enforce all relative lock times via sequence numbers for the stake
 	// transaction tree once the stake vote for the agenda is active.
 	var prevMedianTime time.Time
+	lnFeaturesActive, err := b.isLNFeaturesAgendaActive(node.parent)
+	if err != nil {
+		return err
+	}
 	if lnFeaturesActive {
 		// Use the past median time of the *previous* block in order
 		// to determine if the transactions in the current block are
@@ -3712,8 +3631,7 @@ func (b *BlockChain) checkConnectBlock(node *blockNode, block, parent *dcrutil.B
 		// Skip the coinbase since it does not have any inputs and thus
 		// lock times do not apply.
 		for _, tx := range block.Transactions()[1:] {
-			sequenceLock, err := b.calcSequenceLock(node, tx,
-				legacySeqLockView, true)
+			sequenceLock, err := b.calcSequenceLock(node, tx, view, true)
 			if err != nil {
 				return err
 			}
diff --git a/chaincfg/doc.go b/chaincfg/doc.go
index b709d775..74ad5b7f 100644
--- a/chaincfg/doc.go
+++ b/chaincfg/doc.go
@@ -7,12 +7,6 @@
 // handle errors where input intended for one network is used on an application
 // instance running on a different network.
 //
-// For library packages, chaincfg provides the ability to lookup chain
-// parameters and encoding magics when passed a *Params.  Older APIs not updated
-// to the new convention of passing a *Params may lookup the parameters for a
-// wire.DecredNet using ParamsForNet, but be aware that this usage is
-// deprecated and will be removed from chaincfg in the future.
-//
 // For main packages, a (typically global) var may be assigned the address of
 // one of the standard Param vars for use as the application's "active" network.
 // When a network parameter is needed, it may then be looked up through this
diff --git a/chaincfg/mainnetparams.go b/chaincfg/mainnetparams.go
index 55f2965a..93571ab1 100644
--- a/chaincfg/mainnetparams.go
+++ b/chaincfg/mainnetparams.go
@@ -418,6 +418,9 @@ func MainNetParams() *Params {
 		seeders: []string{
 			"mainnet-seed-1.decred.org",
 			"mainnet-seed-2.decred.org",
+			"mainnet-seed.planetdecred.org",
+			"mainnet-seed.dcrdata.org",
+			"mainnet-seed.jamieholdstock.com",
 		},
 	}
 }
diff --git a/config.go b/config.go
index 4dc14566..07c117d2 100644
--- a/config.go
+++ b/config.go
@@ -79,7 +79,6 @@ const (
 	defaultTxIndex           = false
 	defaultAddrIndex         = false
 	defaultNoExistsAddrIndex = false
-	defaultNoCFilters        = false
 )
 
 var (
@@ -210,8 +209,6 @@ type config struct {
 	DropAddrIndex       bool `long:"dropaddrindex" description:"Deletes the address-based transaction index from the database on start up and then exits"`
 	NoExistsAddrIndex   bool `long:"noexistsaddrindex" description:"Disable the exists address index, which tracks whether or not an address has even been used"`
 	DropExistsAddrIndex bool `long:"dropexistsaddrindex" description:"Deletes the exists address index from the database on start up and then exits"`
-	NoCFilters          bool `long:"nocfilters" description:"(Deprecated) Disable compact filtering (CF) support"`
-	DropCFIndex         bool `long:"dropcfindex" description:"(Deprecated) Deletes the index used for compact filtering (CF) support from the database on start up and then exits"`
 
 	// IPC options.
 	PipeRx         uint `long:"piperx" description:"File descriptor of read end pipe to enable parent -> child process communication"`
@@ -599,7 +596,6 @@ func loadConfig(appName string) (*config, []string, error) {
 		TxIndex:           defaultTxIndex,
 		AddrIndex:         defaultAddrIndex,
 		NoExistsAddrIndex: defaultNoExistsAddrIndex,
-		NoCFilters:        defaultNoCFilters,
 
 		// Cooked options ready for use.
 		ipv4NetInfo:  types.NetworksResult{Name: "IPV4"},
@@ -1045,12 +1041,6 @@ func loadConfig(appName string) (*config, []string, error) {
 		return nil, nil, err
 	}
 
-	// !--nocfilters and --dropcfindex do not mix.
-	if !cfg.NoCFilters && cfg.DropCFIndex {
-		err := errors.New("dropcfindex cannot be activated without nocfilters")
-		return nil, nil, err
-	}
-
 	// Check mining addresses are valid and saved parsed versions.
 	cfg.miningAddrs = make([]dcrutil.Address, 0, len(cfg.MiningAddrs))
 	for _, strAddr := range cfg.MiningAddrs {
diff --git a/dcrd.go b/dcrd.go
index 5ab68860..20e44b6f 100644
--- a/dcrd.go
+++ b/dcrd.go
@@ -177,13 +177,11 @@ func dcrdMain() error {
 
 		return nil
 	}
-	if cfg.DropCFIndex {
-		if err := indexers.DropCfIndex(ctx, db); err != nil {
-			dcrdLog.Errorf("%v", err)
-			return err
-		}
 
-		return nil
+	// Drop the legacy v1 committed filter index if needed.
+	if err := indexers.DropCfIndex(ctx, db); err != nil {
+		dcrdLog.Errorf("%v", err)
+		return err
 	}
 
 	// Create server.
diff --git a/dcrjson/jsonrpcerr.go b/dcrjson/jsonrpcerr.go
index b76d092b..5877fdac 100644
--- a/dcrjson/jsonrpcerr.go
+++ b/dcrjson/jsonrpcerr.go
@@ -71,7 +71,6 @@ const (
 	ErrRPCDifficulty        RPCErrorCode = -5
 	ErrRPCOutOfRange        RPCErrorCode = -1
 	ErrRPCNoTxInfo          RPCErrorCode = -5
-	ErrRPCNoCFIndex         RPCErrorCode = -5
 	ErrRPCNoNewestBlockInfo RPCErrorCode = -5
 	ErrRPCInvalidTxVout     RPCErrorCode = -5
 	ErrRPCNoTreasury        RPCErrorCode = -5
diff --git a/doc.go b/doc.go
index f295522c..9642ab1c 100644
--- a/doc.go
+++ b/doc.go
@@ -174,11 +174,6 @@ Application Options:
                                whether or not an address has even been used
       --dropexistsaddrindex    Deletes the exists address index from the
                                database on start up and then exits
-      --nocfilters             (Deprecated) Disable compact filtering (CF)
-                               support
-      --dropcfindex            (Deprecated) Deletes the index used for compact
-                               filtering (CF) support from the database on start
-                               up and then exits
       --piperx=                File descriptor of read end pipe to enable parent
                                -> child process communication
       --pipetx=                File descriptor of write end pipe to enable
diff --git a/docs/json_rpc_api.mediawiki b/docs/json_rpc_api.mediawiki
index acec9135..b3a8abc1 100644
--- a/docs/json_rpc_api.mediawiki
+++ b/docs/json_rpc_api.mediawiki
@@ -152,7 +152,7 @@ the method name for further details such as parameter and return information.
 |-
 |[[#estimatesmartfee|estimatesmartfee]]
 |Y
-|Returns the estimated fee using the historical fee data in dcr/kb.
+|Returns the estimated fee using the historical fee data in dcr/kb and the block number where the estimate was found.
 |-
 |[[#estimatestakediff|estimatestakediff]]
 |Y
@@ -226,14 +226,6 @@ the method name for further details such as parameter and return information.
 |Y
 |Returns information regarding subsidy amounts.
 |-
-|[[#getcfilter|getcfilter]]
-|Y
-|Returns the committed filter for a block.
-|-
-|[[#getcfilterheader|getcfilterheader]]
-|Y
-|Returns the committed filter header for a block.
-|-
 |[[#getcfilterv2|getcfilterv2]]
 |Y
 |Returns the version 2 block filter for the given block along with a proof that can be used to prove the filter is committed to by the block header.
@@ -737,10 +729,13 @@ the method name for further details such as parameter and return information.
 # <code>mode</code>: <code>(string)</code> The only supported mode for the moment is 'conservative'.
 |-
 !Description
-|Returns the estimated fee using the historical fee data in dcr/kb.
+|Returns the estimated fee using the historical fee data in dcr/kb and the block number where the estimate was found.
 |-
 !Returns
-|<code>numeric</code>
+|<code>{json object}</code>
+: <code>fee</code>: <code>(numeric)</code> The estimated fee.
+: <code>errors</code>: <code>(json array)</code> Unused.
+: <code>blocks</code>: <code>(numeric)</code> The block number where the estimate was found.
 |-
 !Example Return
 |
@@ -1254,59 +1249,6 @@ of the best block.
 
 ----
 
-====getcfilter====
-{|
-!Method
-|getcfilter
-|-
-!Parameters
-|
-# <code>hash</code>: <code>(string, required)</code> The block hash of the filter being queried.
-# <code>filtertype</code>: <code>(string, required)</code> The type of committed filter to return.
-|-
-!Description
-|
-: Returns the committed filter for a block.
-: Deprecated: This will be removed in the next major version.  Use [[#getcfilterv2|getcfilterv2]] instead.
-|-
-!Returns
-|<code>string</code> The hex encoded committed filter.
-|-
-!Example Return
-|
-: Newlines added for display purposes.  The actual return does not contain newlines.
-:<code>0000002305d72c4c0f6e3d19783a59f26cef1fab1ec21585f8016a22a43762cd2edbda7a1fba</code>
-:<code>596d06e092c6ec2f651d5242074fd063918741f4492dd0dd889f6909fd3028e04fa1e84259c4</code>
-:<code>1088aea6fa0cd0cb162daaa4afb05de43718914a7e04</code>
-|}
-
-----
-
-====getcfilterheader====
-{|
-!Method
-|getcfilterheader
-|-
-!Parameters
-|
-# <code>hash</code>: <code>(string, required)</code> The block hash of the filter header being queried.
-# <code>filtertype</code>: <code>(string, required)</code> The type of committed filter header to return.
-|-
-!Description
-|
-: Returns the committed filter header for a block.
-: Deprecated: This will be removed in the next major version.  Use [[#getcfilterv2|getcfilterv2]] instead.
-|-
-!Returns
-|<code>string</code> The hex encoded committed filter header.
-|-
-!Example Return
-|
-<code>bd60b439d72fad354acc4b47d374ec34b0cca3bb25994b459f01f6f84ad6b478</code>
-|}
-
-----
-
 ====getcfilterv2====
 {|
 !Method
diff --git a/gcs/blockcf/README.md b/gcs/blockcf/README.md
deleted file mode 100644
index 73f22ea5..00000000
--- a/gcs/blockcf/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-blockcf
-=======
-
-[![Doc](https://img.shields.io/badge/doc-reference-blue.svg)](https://pkg.go.dev/github.com/decred/dcrd/gcs/v2/blockcf)
-
-## Overview
-
-Package blockcf provides functions to build committed filters from blocks.
-Unlike the gcs package, which is a general implementation of golomb coded sets,
-this package is tailored for specific filter creation for Decred blocks.
diff --git a/gcs/blockcf/blockcf.go b/gcs/blockcf/blockcf.go
deleted file mode 100644
index d14bdc46..00000000
--- a/gcs/blockcf/blockcf.go
+++ /dev/null
@@ -1,216 +0,0 @@
-// Copyright (c) 2017 The btcsuite developers
-// Copyright (c) 2017 The Lightning Network Developers
-// Copyright (c) 2018-2020 The Decred developers
-// Use of this source code is governed by an ISC
-// license that can be found in the LICENSE file.
-
-/*
-Package blockcf provides functions for building committed filters for blocks
-using Golomb-coded sets in a way that is useful for light clients such as SPV
-wallets.
-
-Committed filters are a reversal of how bloom filters are typically used by a
-light client: a consensus-validating full node commits to filters for every
-block with a predetermined collision probability and light clients match against
-the filters locally rather than uploading personal data to other nodes.  If a
-filter matches, the light client should fetch the entire block and further
-inspect it for relevant transactions.
-*/
-package blockcf
-
-import (
-	"encoding/binary"
-
-	"github.com/decred/dcrd/blockchain/stake/v4"
-	"github.com/decred/dcrd/chaincfg/chainhash"
-	"github.com/decred/dcrd/gcs/v3"
-	"github.com/decred/dcrd/txscript/v4"
-	"github.com/decred/dcrd/wire"
-)
-
-const (
-	// P is the collision probability used for block committed filters (2^-20)
-	P = 20
-)
-
-// Entries describes all of the filter entries used to create a GCS filter and
-// provides methods for appending data structures found in blocks.
-type Entries [][]byte
-
-// AddOutPoint adds a serialized outpoint to an entries slice.
-func (e *Entries) AddOutPoint(outpoint *wire.OutPoint) {
-	entry := make([]byte, chainhash.HashSize+4)
-	copy(entry, outpoint.Hash[:])
-	binary.LittleEndian.PutUint32(entry[chainhash.HashSize:], outpoint.Index)
-
-	*e = append(*e, entry)
-}
-
-// AddHash adds a hash to an entries slice.
-func (e *Entries) AddHash(hash *chainhash.Hash) {
-	*e = append(*e, hash[:])
-}
-
-// AddRegularPkScript adds the regular tx output script to an entries slice.
-func (e *Entries) AddRegularPkScript(script []byte) {
-	*e = append(*e, script)
-}
-
-// AddStakePkScript adds the output script without the stake opcode tag to an
-// entries slice.
-func (e *Entries) AddStakePkScript(script []byte) {
-	*e = append(*e, script[1:])
-}
-
-// AddSigScript adds any data pushes of a signature script to an entries slice.
-func (e *Entries) AddSigScript(script []byte) {
-	const scriptVersion = 0
-
-	// Don't add anything if the script does not fully parse without error.
-	tokenizer := txscript.MakeScriptTokenizer(scriptVersion, script)
-	for tokenizer.Next() {
-		// Nothing to do.
-	}
-	if tokenizer.Err() != nil {
-		return
-	}
-
-	// Add any non-empty pushed data.
-	tokenizer = txscript.MakeScriptTokenizer(scriptVersion, script)
-	for tokenizer.Next() {
-		data := tokenizer.Data()
-		if len(data) != 0 {
-			*e = append(*e, data)
-		}
-	}
-}
-
-// Key creates a block committed filter key by truncating a block hash to the
-// key size.
-func Key(hash *chainhash.Hash) [gcs.KeySize]byte {
-	var key [gcs.KeySize]byte
-	copy(key[:], hash[:])
-	return key
-}
-
-// Regular builds a regular GCS filter from a block.  A regular GCS filter will
-// contain all the previous regular outpoints spent within a block, as well as
-// the data pushes within all the outputs created within a block which can be
-// spent by regular transactions.
-func Regular(block *wire.MsgBlock) (*gcs.FilterV1, error) {
-	var data Entries
-
-	// Add "regular" data from stake transactions.  For each class of stake
-	// transaction, the following data is committed to the regular filter:
-	//
-	//   ticket purchases:
-	//     - all previous outpoints
-	//     - all change output scripts
-	//
-	//   votes:
-	//     - all OP_SSGEN-tagged output scripts (all outputs after the first
-	//       two -- these describe the block voted on and the vote choices)
-	//
-	//   revocations:
-	//     - all output scripts
-	//
-	// Because change outputs are required in a ticket purchase, even when
-	// unused, a special case is made that excludes their commitment when the
-	// output value is zero (provably unspendable).
-	//
-	// Output scripts are handled specially for stake transactions by slicing
-	// off the stake opcode tag (OP_SS*).  This tag always appears as the first
-	// byte of the script and removing it allows users of the filter to only
-	// match against a normal P2PKH or P2SH script, instead of many extra
-	// matches for each tag.
-	for _, tx := range block.STransactions {
-		// It is ok to harcode isTreasuryEnabled to false since the
-		// transactions that used cfilters v1 will never have
-		// treasury opcodes.
-		switch stake.DetermineTxType(tx, false) {
-		case stake.TxTypeSStx: // Ticket purchase
-			for _, in := range tx.TxIn {
-				data.AddOutPoint(&in.PreviousOutPoint)
-			}
-			for i := 2; i < len(tx.TxOut); i += 2 { // Iterate change outputs
-				out := tx.TxOut[i]
-				if out.Value != 0 {
-					data.AddStakePkScript(out.PkScript)
-				}
-			}
-
-		case stake.TxTypeSSGen: // Vote
-			for _, out := range tx.TxOut[2:] { // Iterate generated coins
-				data.AddStakePkScript(out.PkScript)
-			}
-
-		case stake.TxTypeSSRtx: // Revocation
-			for _, out := range tx.TxOut {
-				data.AddStakePkScript(out.PkScript)
-			}
-		}
-	}
-
-	// For regular transactions, all previous outpoints except the coinbase's
-	// are committed, and all output scripts are committed.
-	for i, tx := range block.Transactions {
-		if i != 0 {
-			for _, txIn := range tx.TxIn {
-				data.AddOutPoint(&txIn.PreviousOutPoint)
-			}
-		}
-		for _, txOut := range tx.TxOut {
-			data.AddRegularPkScript(txOut.PkScript)
-		}
-	}
-
-	// Create the key by truncating the block hash.
-	blockHash := block.BlockHash()
-	key := Key(&blockHash)
-
-	return gcs.NewFilterV1(P, key, data)
-}
-
-// Extended builds an extended GCS filter from a block.  An extended filter
-// supplements a regular basic filter by including all transaction hashes of
-// regular and stake transactions, and adding the witness data (a.k.a. the
-// signature script) found within every non-coinbase regular transaction.
-func Extended(block *wire.MsgBlock) (*gcs.FilterV1, error) {
-	var data Entries
-
-	// For each stake transaction, commit the transaction hash.  If the
-	// transaction is a ticket purchase, commit pushes from the signature script
-	// (witness).
-	for _, tx := range block.STransactions {
-		txHash := tx.TxHash()
-		data.AddHash(&txHash)
-
-		if stake.IsSStx(tx) {
-			for _, in := range tx.TxIn {
-				data.AddSigScript(in.SignatureScript)
-			}
-		}
-	}
-
-	// For each regular transaction, commit the transaction hash.  For all
-	// regular transactions except the coinbase, commit pushes to the signature
-	// script (witness).
-	coinbaseHash := block.Transactions[0].TxHash()
-	data.AddHash(&coinbaseHash)
-	for _, tx := range block.Transactions[1:] {
-		txHash := tx.TxHash()
-		data.AddHash(&txHash)
-
-		for _, txIn := range tx.TxIn {
-			if txIn.SignatureScript != nil {
-				data.AddSigScript(txIn.SignatureScript)
-			}
-		}
-	}
-
-	// Create the key by truncating the block hash.
-	blockHash := block.BlockHash()
-	key := Key(&blockHash)
-
-	return gcs.NewFilterV1(P, key, data)
-}
diff --git a/internal/rpcserver/interface.go b/internal/rpcserver/interface.go
index fbfe0626..af7126e2 100644
--- a/internal/rpcserver/interface.go
+++ b/internal/rpcserver/interface.go
@@ -530,24 +530,6 @@ type BlockTemplater interface {
 	UpdateBlockTime(header *wire.BlockHeader) error
 }
 
-// Filterer provides an interface for retrieving a block's committed filter or
-// committed filter header.
-//
-// The interface contract requires that all of these methods are safe for
-// concurrent access.
-//
-// Deprecated: This will be removed in the next major version.  Use FiltererV2
-// instead.
-type Filterer interface {
-	// FilterByBlockHash returns the serialized contents of a block's regular or
-	// extended committed filter.
-	FilterByBlockHash(h *chainhash.Hash, filterType wire.FilterType) ([]byte, error)
-
-	// FilterHeaderByBlockHash returns the serialized contents of a block's regular
-	// or extended committed filter header.
-	FilterHeaderByBlockHash(h *chainhash.Hash, filterType wire.FilterType) ([]byte, error)
-}
-
 // FiltererV2 provides an interface for retrieving a block's version 2 GCS
 // filter.
 //
diff --git a/internal/rpcserver/rpcserver.go b/internal/rpcserver/rpcserver.go
index c42b9020..dcdef31d 100644
--- a/internal/rpcserver/rpcserver.go
+++ b/internal/rpcserver/rpcserver.go
@@ -171,8 +171,6 @@ var rpcHandlersBeforeInit = map[types.Method]commandHandler{
 	"getblockhash":          handleGetBlockHash,
 	"getblockheader":        handleGetBlockHeader,
 	"getblocksubsidy":       handleGetBlockSubsidy,
-	"getcfilter":            handleGetCFilter,
-	"getcfilterheader":      handleGetCFilterHeader,
 	"getcfilterv2":          handleGetCFilterV2,
 	"getchaintips":          handleGetChainTips,
 	"getcoinsupply":         handleGetCoinSupply,
@@ -350,7 +348,6 @@ var rpcLimited = map[string]struct{}{
 	"getblockhash":          {},
 	"getblockheader":        {},
 	"getblocksubsidy":       {},
-	"getcfilter":            {},
 	"getcfilterv2":          {},
 	"getchaintips":          {},
 	"getcoinsupply":         {},
@@ -1420,7 +1417,10 @@ func handleEstimateSmartFee(_ context.Context, s *Server, cmd interface{}) (inte
 		return nil, rpcInternalError(err.Error(), "Could not estimate fee")
 	}
 
-	return fee.ToCoin(), nil
+	return &types.EstimateSmartFeeResult{
+		FeeRate: fee.ToCoin(),
+		Blocks:  c.Confirmations,
+	}, nil
 }
 
 // handleEstimateStakeDiff implements the estimatestakediff command.
@@ -2297,99 +2297,6 @@ func handleGetHashesPerSec(_ context.Context, s *Server, _ interface{}) (interfa
 	return int64(s.cfg.CPUMiner.HashesPerSecond()), nil
 }
 
-// handleGetCFilter implements the getcfilter command.
-//
-// Deprecated: This will be removed in the next major version.  Use
-// handleGetCFilterV2 instead.
-func handleGetCFilter(_ context.Context, s *Server, cmd interface{}) (interface{}, error) {
-	if s.cfg.Filterer == nil {
-		return nil, &dcrjson.RPCError{
-			Code:    dcrjson.ErrRPCNoCFIndex,
-			Message: "Compact filters must be enabled for this command",
-		}
-	}
-
-	c := cmd.(*types.GetCFilterCmd)
-	hash, err := chainhash.NewHashFromStr(c.Hash)
-	if err != nil {
-		return nil, rpcDecodeHexError(c.Hash)
-	}
-
-	var filterType wire.FilterType
-	switch c.FilterType {
-	case "regular":
-		filterType = wire.GCSFilterRegular
-	case "extended":
-		filterType = wire.GCSFilterExtended
-	default:
-		return nil, rpcInvalidError("Unknown filter type %q", c.FilterType)
-	}
-
-	filterBytes, err := s.cfg.Filterer.FilterByBlockHash(hash, filterType)
-	if err != nil {
-		context := fmt.Sprintf("Failed to load %v filter for block %v",
-			filterType, hash)
-		return "", rpcInternalError(err.Error(), context)
-	}
-	if len(filterBytes) == 0 {
-		return nil, &dcrjson.RPCError{
-			Code:    dcrjson.ErrRPCBlockNotFound,
-			Message: fmt.Sprintf("Block not found: %v", hash),
-		}
-	}
-
-	log.Debugf("Found committed filter for %v", hash)
-	return hex.EncodeToString(filterBytes), nil
-}
-
-// handleGetCFilterHeader implements the getcfilterheader command.
-//
-// Deprecated: This will be removed in the next major version.  Use
-// handleGetCFilterV2 instead.
-func handleGetCFilterHeader(_ context.Context, s *Server, cmd interface{}) (interface{}, error) {
-	if s.cfg.Filterer == nil {
-		return nil, &dcrjson.RPCError{
-			Code:    dcrjson.ErrRPCNoCFIndex,
-			Message: "The CF index must be enabled for this command",
-		}
-	}
-
-	c := cmd.(*types.GetCFilterHeaderCmd)
-	hash, err := chainhash.NewHashFromStr(c.Hash)
-	if err != nil {
-		return nil, rpcDecodeHexError(c.Hash)
-	}
-
-	var filterType wire.FilterType
-	switch c.FilterType {
-	case "regular":
-		filterType = wire.GCSFilterRegular
-	case "extended":
-		filterType = wire.GCSFilterExtended
-	default:
-		return nil, rpcInvalidError("Unknown filter type %q",
-			c.FilterType)
-	}
-
-	headerBytes, err := s.cfg.Filterer.FilterHeaderByBlockHash(hash, filterType)
-	if err != nil {
-		context := fmt.Sprintf("Failed to load %v filter header for block %v",
-			filterType, hash)
-		return "", rpcInternalError(err.Error(), context)
-	}
-	if bytes.Equal(headerBytes, zeroHash[:]) && *hash !=
-		s.cfg.ChainParams.GenesisHash {
-
-		return nil, &dcrjson.RPCError{
-			Code:    dcrjson.ErrRPCBlockNotFound,
-			Message: fmt.Sprintf("Block not found: %v", hash),
-		}
-	}
-
-	hash.SetBytes(headerBytes)
-	return hash.String(), nil
-}
-
 // handleGetHeaders implements the getheaders command.
 func handleGetHeaders(_ context.Context, s *Server, cmd interface{}) (interface{}, error) {
 	c := cmd.(*types.GetHeadersCmd)
@@ -6229,9 +6136,6 @@ type Config struct {
 	// LogManager defines the log manager for the RPC server to use.
 	LogManager LogManager
 
-	// Filterer defines the filterer for the RPC server to use.
-	Filterer Filterer
-
 	// FiltererV2 defines the V2 filterer for the RPC server to use.
 	FiltererV2 FiltererV2
 }
diff --git a/internal/rpcserver/rpcserverhelp.go b/internal/rpcserver/rpcserverhelp.go
index 64a5103a..2fdaa45c 100644
--- a/internal/rpcserver/rpcserverhelp.go
+++ b/internal/rpcserver/rpcserverhelp.go
@@ -380,20 +380,6 @@ var helpDescsEnUS = map[string]string{
 	"getblocksubsidyresult-pow":       "The Proof-of-Work subsidy",
 	"getblocksubsidyresult-total":     "The total subsidy",
 
-	// GetCFilterCmd help.
-	"getcfilter--synopsis": "Returns the committed filter for a block.\n\n" +
-		"Deprecated: Use handleGetCFilterV2 instead.",
-	"getcfilter--result0":   "The committed filter serialized with the N value and encoded as a hex string",
-	"getcfilter-hash":       "The block hash of the filter being queried",
-	"getcfilter-filtertype": "The type of committed filter to return",
-
-	// GetCFilterHeaderCmd help.
-	"getcfilterheader--synopsis": "Returns the filter header hash committing to all filters in the chain up through a block.\n\n" +
-		"Deprecated: Use handleGetCFilterV2 instead.",
-	"getcfilterheader--result0":   "The filter header commitment hash",
-	"getcfilterheader-hash":       "The block hash of the filter header being queried",
-	"getcfilterheader-filtertype": "The type of committed filter to return the header commitment for",
-
 	// GetCFilterV2Cmd help.
 	"getcfilterv2--synopsis": "Returns the version 2 block filter for the given block along with a proof that can be used to prove the filter is committed to by the block header",
 	"getcfilterv2-blockhash": "The block hash of the filter to retrieve",
@@ -882,7 +868,9 @@ var helpDescsEnUS = map[string]string{
 	"estimatesmartfee--synopsis":     "Returns the estimated fee using the historical fee data in dcr/kb.",
 	"estimatesmartfee-confirmations": "Estimate the fee rate a transaction requires so that it is mined in up to this number of blocks.",
 	"estimatesmartfee-mode":          "The only supported mode for the moment is 'conservative'.",
-	"estimatesmartfee--result0":      "Estimated fee rate (in DCR/KB).",
+	"estimatesmartfeeresult-feerate": "The Estimated fee rate (in DCR/KB).",
+	"estimatesmartfeeresult-errors":  "Unused.",
+	"estimatesmartfeeresult-blocks":  "The block number where the estimate was found.",
 
 	// EstimateStakeDiff help.
 	"estimatestakediff--synopsis":      "Estimate the next minimum, maximum, expected, and user-specified stake difficulty",
@@ -997,7 +985,7 @@ var rpcResultTypes = map[types.Method][]interface{}{
 	"decoderawtransaction":  {(*types.TxRawDecodeResult)(nil)},
 	"decodescript":          {(*types.DecodeScriptResult)(nil)},
 	"estimatefee":           {(*float64)(nil)},
-	"estimatesmartfee":      {(*float64)(nil)},
+	"estimatesmartfee":      {(*types.EstimateSmartFeeResult)(nil)},
 	"estimatestakediff":     {(*types.EstimateStakeDiffResult)(nil)},
 	"existsaddress":         {(*bool)(nil)},
 	"existsaddresses":       {(*string)(nil)},
@@ -1016,8 +1004,6 @@ var rpcResultTypes = map[types.Method][]interface{}{
 	"getblockhash":          {(*string)(nil)},
 	"getblockheader":        {(*string)(nil), (*types.GetBlockHeaderVerboseResult)(nil)},
 	"getblocksubsidy":       {(*types.GetBlockSubsidyResult)(nil)},
-	"getcfilter":            {(*string)(nil)},
-	"getcfilterheader":      {(*string)(nil)},
 	"getcfilterv2":          {(*types.GetCFilterV2Result)(nil)},
 	"getchaintips":          {(*[]types.GetChainTipsResult)(nil)},
 	"getconnectioncount":    {(*int32)(nil)},
diff --git a/rpc/jsonrpc/types/chainsvrcmds.go b/rpc/jsonrpc/types/chainsvrcmds.go
index 249f1eae..0491d60e 100644
--- a/rpc/jsonrpc/types/chainsvrcmds.go
+++ b/rpc/jsonrpc/types/chainsvrcmds.go
@@ -460,36 +460,6 @@ func NewGetBlockSubsidyCmd(height int64, voters uint16) *GetBlockSubsidyCmd {
 	}
 }
 
-// GetCFilterCmd defines the getcfilter JSON-RPC command.
-type GetCFilterCmd struct {
-	Hash       string
-	FilterType string
-}
-
-// NewGetCFilterCmd returns a new instance which can be used to issue a
-// getcfilter JSON-RPC command.
-func NewGetCFilterCmd(hash string, filterType string) *GetCFilterCmd {
-	return &GetCFilterCmd{
-		Hash:       hash,
-		FilterType: filterType,
-	}
-}
-
-// GetCFilterHeaderCmd defines the getcfilterheader JSON-RPC command.
-type GetCFilterHeaderCmd struct {
-	Hash       string
-	FilterType string
-}
-
-// NewGetCFilterHeaderCmd returns a new instance which can be used to issue a
-// getcfilterheader JSON-RPC command.
-func NewGetCFilterHeaderCmd(hash string, filterType string) *GetCFilterHeaderCmd {
-	return &GetCFilterHeaderCmd{
-		Hash:       hash,
-		FilterType: filterType,
-	}
-}
-
 // GetCFilterV2Cmd defines the getcfilterv2 JSON-RPC command.
 type GetCFilterV2Cmd struct {
 	BlockHash string
@@ -1180,8 +1150,6 @@ func init() {
 	dcrjson.MustRegister(Method("getblockhash"), (*GetBlockHashCmd)(nil), flags)
 	dcrjson.MustRegister(Method("getblockheader"), (*GetBlockHeaderCmd)(nil), flags)
 	dcrjson.MustRegister(Method("getblocksubsidy"), (*GetBlockSubsidyCmd)(nil), flags)
-	dcrjson.MustRegister(Method("getcfilter"), (*GetCFilterCmd)(nil), flags)
-	dcrjson.MustRegister(Method("getcfilterheader"), (*GetCFilterHeaderCmd)(nil), flags)
 	dcrjson.MustRegister(Method("getcfilterv2"), (*GetCFilterV2Cmd)(nil), flags)
 	dcrjson.MustRegister(Method("getchaintips"), (*GetChainTipsCmd)(nil), flags)
 	dcrjson.MustRegister(Method("getcoinsupply"), (*GetCoinSupplyCmd)(nil), flags)
diff --git a/rpc/jsonrpc/types/chainsvrresults.go b/rpc/jsonrpc/types/chainsvrresults.go
index c9d6cd90..f4566b7e 100644
--- a/rpc/jsonrpc/types/chainsvrresults.go
+++ b/rpc/jsonrpc/types/chainsvrresults.go
@@ -28,14 +28,9 @@ type DecodeScriptResult struct {
 
 // EstimateSmartFeeResult models the data returned from the estimatesmartfee
 // command.
-//
-// NOTE: EstimateSmartFee currently returns a float. This result type is
-// unused.
-//
-// TODO: Rework EstimateSmartFee to return this result type.
 type EstimateSmartFeeResult struct {
 	FeeRate float64  `json:"feerate"`
-	Errors  []string `json:"errors"`
+	Errors  []string `json:"errors,omitempty"`
 	Blocks  int64    `json:"blocks"`
 }
 
diff --git a/rpcclient/chain.go b/rpcclient/chain.go
index 1fd5a821..a471a253 100644
--- a/rpcclient/chain.go
+++ b/rpcclient/chain.go
@@ -10,13 +10,11 @@ import (
 	"context"
 	"encoding/hex"
 	"encoding/json"
-	"errors"
 
 	"github.com/decred/dcrd/chaincfg/chainhash"
 	"github.com/decred/dcrd/dcrjson/v3"
 	"github.com/decred/dcrd/dcrutil/v4"
 	"github.com/decred/dcrd/gcs/v3"
-	"github.com/decred/dcrd/gcs/v3/blockcf"
 	"github.com/decred/dcrd/gcs/v3/blockcf2"
 	chainjson "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
 	"github.com/decred/dcrd/wire"
@@ -899,103 +897,6 @@ func (c *Client) Rescan(ctx context.Context, blockHashes []chainhash.Hash) (*cha
 	return c.RescanAsync(ctx, blockHashes).Receive()
 }
 
-// FutureGetCFilterResult is a future promise to deliver the result of a
-// GetCFilterAsync RPC invocation (or an applicable error).
-type FutureGetCFilterResult cmdRes
-
-// Receive waits for the response promised by the future and returns the
-// discovered rescan data.
-func (r *FutureGetCFilterResult) Receive() (*gcs.FilterV1, error) {
-	res, err := receiveFuture(r.ctx, r.c)
-	if err != nil {
-		return nil, err
-	}
-
-	var filterHex string
-	err = json.Unmarshal(res, &filterHex)
-	if err != nil {
-		return nil, err
-	}
-	filterBytes, err := hex.DecodeString(filterHex)
-	if err != nil {
-		return nil, err
-	}
-
-	return gcs.FromBytesV1(blockcf.P, filterBytes)
-}
-
-// GetCFilterAsync returns an instance of a type that can be used to get the
-// result of the RPC at some future time by invoking the Receive function on the
-// returned instance.
-//
-// See GetCFilter for the blocking version and more details.
-func (c *Client) GetCFilterAsync(ctx context.Context, blockHash *chainhash.Hash, filterType wire.FilterType) *FutureGetCFilterResult {
-	var ft string
-	switch filterType {
-	case wire.GCSFilterRegular:
-		ft = "regular"
-	case wire.GCSFilterExtended:
-		ft = "extended"
-	default:
-		return (*FutureGetCFilterResult)(newFutureError(ctx, errors.New("unknown filter type")))
-	}
-
-	cmd := chainjson.NewGetCFilterCmd(blockHash.String(), ft)
-	return (*FutureGetCFilterResult)(c.sendCmd(ctx, cmd))
-}
-
-// GetCFilter returns the committed filter of type filterType for a block.
-func (c *Client) GetCFilter(ctx context.Context, blockHash *chainhash.Hash, filterType wire.FilterType) (*gcs.FilterV1, error) {
-	return c.GetCFilterAsync(ctx, blockHash, filterType).Receive()
-}
-
-// FutureGetCFilterHeaderResult is a future promise to deliver the result of a
-// GetCFilterHeaderAsync RPC invocation (or an applicable error).
-type FutureGetCFilterHeaderResult cmdRes
-
-// Receive waits for the response promised by the future and returns the
-// discovered rescan data.
-func (r *FutureGetCFilterHeaderResult) Receive() (*chainhash.Hash, error) {
-	res, err := receiveFuture(r.ctx, r.c)
-	if err != nil {
-		return nil, err
-	}
-
-	var filterHeaderHex string
-	err = json.Unmarshal(res, &filterHeaderHex)
-	if err != nil {
-		return nil, err
-	}
-
-	return chainhash.NewHashFromStr(filterHeaderHex)
-}
-
-// GetCFilterHeaderAsync returns an instance of a type that can be used to get
-// the result of the RPC at some future time by invoking the Receive function on
-// the returned instance.
-//
-// See GetCFilterHeader for the blocking version and more details.
-func (c *Client) GetCFilterHeaderAsync(ctx context.Context, blockHash *chainhash.Hash, filterType wire.FilterType) *FutureGetCFilterHeaderResult {
-	var ft string
-	switch filterType {
-	case wire.GCSFilterRegular:
-		ft = "regular"
-	case wire.GCSFilterExtended:
-		ft = "extended"
-	default:
-		return (*FutureGetCFilterHeaderResult)(newFutureError(ctx, errors.New("unknown filter type")))
-	}
-
-	cmd := chainjson.NewGetCFilterHeaderCmd(blockHash.String(), ft)
-	return (*FutureGetCFilterHeaderResult)(c.sendCmd(ctx, cmd))
-}
-
-// GetCFilterHeader returns the committed filter header hash of type filterType
-// for a block.
-func (c *Client) GetCFilterHeader(ctx context.Context, blockHash *chainhash.Hash, filterType wire.FilterType) (*chainhash.Hash, error) {
-	return c.GetCFilterHeaderAsync(ctx, blockHash, filterType).Receive()
-}
-
 // CFilterV2Result is the result of calling the GetCFilterV2 and
 // GetCFilterV2Async methods.
 type CFilterV2Result struct {
diff --git a/server.go b/server.go
index a19560ce..73b94065 100644
--- a/server.go
+++ b/server.go
@@ -36,8 +36,6 @@ import (
 	"github.com/decred/dcrd/connmgr/v3"
 	"github.com/decred/dcrd/database/v2"
 	"github.com/decred/dcrd/dcrutil/v4"
-	"github.com/decred/dcrd/gcs/v3"
-	"github.com/decred/dcrd/gcs/v3/blockcf"
 	"github.com/decred/dcrd/internal/fees"
 	"github.com/decred/dcrd/internal/mempool"
 	"github.com/decred/dcrd/internal/mining"
@@ -54,7 +52,7 @@ import (
 const (
 	// defaultServices describes the default services that are supported by
 	// the server.
-	defaultServices = wire.SFNodeNetwork | wire.SFNodeCF
+	defaultServices = wire.SFNodeNetwork
 
 	// defaultRequiredServices describes the default services that are
 	// required to be supported by outbound peers.
@@ -490,7 +488,6 @@ type server struct {
 	txIndex         *indexers.TxIndex
 	addrIndex       *indexers.AddrIndex
 	existsAddrIndex *indexers.ExistsAddrIndex
-	cfIndex         *indexers.CFIndex
 
 	// The following fields are used to filter duplicate block announcements.
 	announcedBlockMtx sync.Mutex
@@ -1225,109 +1222,38 @@ func (sp *serverPeer) OnGetHeaders(p *peer.Peer, msg *wire.MsgGetHeaders) {
 	p.QueueMessage(&wire.MsgHeaders{Headers: blockHeaders}, nil)
 }
 
-// enforceNodeCFFlag disconnects the peer if the server is not configured to
-// allow committed filters.  Additionally, if the peer has negotiated to a
-// protocol version that is high enough to observe the committed filter service
-// support bit, it will be banned since it is intentionally violating the
-// protocol.
-func (sp *serverPeer) enforceNodeCFFlag(cmd string) bool {
-	if !hasServices(sp.server.services, wire.SFNodeCF) {
-		// Ban the peer if the protocol version is high enough that the peer is
-		// knowingly violating the protocol and banning is enabled.
-		//
-		// NOTE: Even though the addBanScore function already examines whether
-		// or not banning is enabled, it is checked here as well to ensure the
-		// violation is logged and the peer is disconnected regardless.
-		if sp.ProtocolVersion() >= wire.NodeCFVersion && !cfg.DisableBanning {
-			// Disconnect the peer regardless of whether it was banned.
-			sp.addBanScore(100, 0, cmd)
-			sp.Disconnect()
-			return false
-		}
-
-		// Disconnect the peer regardless of protocol version or banning state.
-		peerLog.Debugf("%s sent an unsupported %s request -- disconnecting", sp,
-			cmd)
+// enforceNodeCFFlag bans the peer if it has negotiated to a protocol version
+// that is high enough to observe the committed filter service support bit since
+// it is intentionally violating the protocol by requesting one from when the
+// server does not advertise support for it.
+//
+// It disconnects the peer when it has negotiated to a protocol version prior to
+// being able to understand the service bit.
+func (sp *serverPeer) enforceNodeCFFlag(cmd string) {
+	// Ban the peer if the protocol version is high enough that the peer is
+	// knowingly violating the protocol and banning is enabled.
+	//
+	// NOTE: Even though the addBanScore function already examines whether
+	// or not banning is enabled, it is checked here as well to ensure the
+	// violation is logged and the peer is disconnected regardless.
+	if sp.ProtocolVersion() >= wire.NodeCFVersion && !cfg.DisableBanning {
+		// Disconnect the peer regardless of whether it was banned.
+		sp.addBanScore(100, 0, cmd)
 		sp.Disconnect()
-		return false
+		return
 	}
 
-	return true
+	// Disconnect the peer regardless of protocol version or banning state.
+	peerLog.Debugf("%s sent an unsupported %s request -- disconnecting", sp,
+		cmd)
+	sp.Disconnect()
 }
 
 // OnGetCFilter is invoked when a peer receives a getcfilter wire message.
 func (sp *serverPeer) OnGetCFilter(p *peer.Peer, msg *wire.MsgGetCFilter) {
 	// Disconnect and/or ban depending on the node cf services flag and
 	// negotiated protocol version.
-	if !sp.enforceNodeCFFlag(msg.Command()) {
-		return
-	}
-
-	// Ignore request if CFs are disabled or the chain is not yet synced.
-	if cfg.NoCFilters || !sp.server.syncManager.IsCurrent() {
-		return
-	}
-
-	// Check for understood filter type.
-	switch msg.FilterType {
-	case wire.GCSFilterRegular, wire.GCSFilterExtended:
-	default:
-		peerLog.Warnf("OnGetCFilter: unsupported filter type '%v' from %s",
-			msg.FilterType, sp)
-
-		// Ban peers requesting unsupported filter types.
-		sp.server.BanPeer(sp)
-		return
-	}
-
-	filterBytes, err := sp.server.cfIndex.FilterByBlockHash(&msg.BlockHash,
-		msg.FilterType)
-	if err != nil {
-		peerLog.Errorf("OnGetCFilter: failed to fetch cfilter: %v", err)
-		return
-	}
-
-	// If the filter is not saved in the index (perhaps it was removed as a
-	// block was disconnected, or this has always been a sidechain block) build
-	// the filter on the spot.
-	if len(filterBytes) == 0 {
-		block, err := sp.server.chain.BlockByHash(&msg.BlockHash)
-		if err != nil {
-			peerLog.Errorf("OnGetCFilter: failed to fetch non-mainchain "+
-				"block %v: %v", &msg.BlockHash, err)
-			return
-		}
-
-		var f *gcs.FilterV1
-		switch msg.FilterType {
-		case wire.GCSFilterRegular:
-			f, err = blockcf.Regular(block.MsgBlock())
-			if err != nil {
-				peerLog.Errorf("OnGetCFilter: failed to build regular "+
-					"cfilter for block %v: %v", &msg.BlockHash, err)
-				return
-			}
-		case wire.GCSFilterExtended:
-			f, err = blockcf.Extended(block.MsgBlock())
-			if err != nil {
-				peerLog.Errorf("OnGetCFilter: failed to build extended "+
-					"cfilter for block %v: %v", &msg.BlockHash, err)
-				return
-			}
-		default:
-			peerLog.Errorf("OnGetCFilter: unhandled filter type %d",
-				msg.FilterType)
-			return
-		}
-
-		filterBytes = f.Bytes()
-	}
-
-	peerLog.Tracef("Obtained CF for %v", &msg.BlockHash)
-
-	filterMsg := wire.NewMsgCFilter(&msg.BlockHash, msg.FilterType,
-		filterBytes)
-	sp.QueueMessage(filterMsg, nil)
+	sp.enforceNodeCFFlag(msg.Command())
 }
 
 // OnGetCFilterV2 is invoked when a peer receives a getcfilterv2 wire message.
@@ -1361,86 +1287,14 @@ func (sp *serverPeer) OnGetCFilterV2(_ *peer.Peer, msg *wire.MsgGetCFilterV2) {
 func (sp *serverPeer) OnGetCFHeaders(p *peer.Peer, msg *wire.MsgGetCFHeaders) {
 	// Disconnect and/or ban depending on the node cf services flag and
 	// negotiated protocol version.
-	if !sp.enforceNodeCFFlag(msg.Command()) {
-		return
-	}
-
-	// Ignore request if CFs are disabled or the chain is not yet synced.
-	if cfg.NoCFilters || !sp.server.syncManager.IsCurrent() {
-		return
-	}
-
-	// Check for understood filter type.
-	switch msg.FilterType {
-	case wire.GCSFilterRegular, wire.GCSFilterExtended:
-	default:
-		peerLog.Warnf("OnGetCFilter: unsupported filter type '%v' from %s",
-			msg.FilterType, sp)
-
-		// Ban peers requesting unsupported filter types.
-		sp.server.BanPeer(sp)
-		return
-	}
-
-	// Find the most recent known block in the best chain based on the block
-	// locator and fetch all of the block hashes after it until either
-	// wire.MaxCFHeadersPerMsg have been fetched or the provided stop hash is
-	// encountered.
-	//
-	// Use the block after the genesis block if no other blocks in the provided
-	// locator are known.  This does mean the served filter headers will start
-	// over at the genesis block if unknown block locators are provided.
-	chain := sp.server.chain
-	hashList := chain.LocateBlocks(msg.BlockLocatorHashes, &msg.HashStop,
-		wire.MaxCFHeadersPerMsg)
-	if len(hashList) == 0 {
-		return
-	}
-
-	// Generate cfheaders message and send it.
-	cfIndex := sp.server.cfIndex
-	headersMsg := wire.NewMsgCFHeaders()
-	for i := range hashList {
-		// Fetch the raw committed filter header bytes from the database.
-		hash := &hashList[i]
-		headerBytes, err := cfIndex.FilterHeaderByBlockHash(hash,
-			msg.FilterType)
-		if err != nil || len(headerBytes) == 0 {
-			peerLog.Warnf("Could not obtain CF header for %v: %v", hash, err)
-			return
-		}
-
-		// Deserialize the hash.
-		var header chainhash.Hash
-		err = header.SetBytes(headerBytes)
-		if err != nil {
-			peerLog.Warnf("Committed filter header deserialize failed: %v", err)
-			return
-		}
-
-		headersMsg.AddCFHeader(&header)
-	}
-	headersMsg.FilterType = msg.FilterType
-	headersMsg.StopHash = hashList[len(hashList)-1]
-	sp.QueueMessage(headersMsg, nil)
+	sp.enforceNodeCFFlag(msg.Command())
 }
 
 // OnGetCFTypes is invoked when a peer receives a getcftypes wire message.
 func (sp *serverPeer) OnGetCFTypes(p *peer.Peer, msg *wire.MsgGetCFTypes) {
 	// Disconnect and/or ban depending on the node cf services flag and
 	// negotiated protocol version.
-	if !sp.enforceNodeCFFlag(msg.Command()) {
-		return
-	}
-
-	// Ignore request if CFs are disabled.
-	if cfg.NoCFilters {
-		return
-	}
-
-	cfTypesMsg := wire.NewMsgCFTypes([]wire.FilterType{
-		wire.GCSFilterRegular, wire.GCSFilterExtended})
-	sp.QueueMessage(cfTypesMsg, nil)
+	sp.enforceNodeCFFlag(msg.Command())
 }
 
 // OnGetAddr is invoked when a peer receives a getaddr wire message and is used
@@ -3449,18 +3303,15 @@ func setupRPCListeners() ([]net.Listener, error) {
 // decred network type specified by chainParams.  Use start to begin accepting
 // connections from peers.
 func newServer(ctx context.Context, listenAddrs []string, db database.DB, chainParams *chaincfg.Params, dataDir string) (*server, error) {
-	services := defaultServices
-	if cfg.NoCFilters {
-		services &^= wire.SFNodeCF
-	}
-
 	amgr := addrmgr.New(cfg.DataDir, dcrdLookup)
+	services := defaultServices
 
 	var listeners []net.Listener
 	var nat *upnpNAT
 	if !cfg.DisableListen {
 		var err error
-		listeners, nat, err = initListeners(ctx, chainParams, amgr, listenAddrs, services)
+		listeners, nat, err = initListeners(ctx, chainParams, amgr, listenAddrs,
+			services)
 		if err != nil {
 			return nil, err
 		}
@@ -3526,11 +3377,6 @@ func newServer(ctx context.Context, listenAddrs []string, db database.DB, chainP
 		s.existsAddrIndex = indexers.NewExistsAddrIndex(db, chainParams)
 		indexes = append(indexes, s.existsAddrIndex)
 	}
-	if !cfg.NoCFilters {
-		indxLog.Info("CF index is enabled")
-		s.cfIndex = indexers.NewCfIndex(db, chainParams)
-		indexes = append(indexes, s.cfIndex)
-	}
 
 	feC := fees.EstimatorConfig{
 		MinBucketFee: cfg.minRelayTxFee,
@@ -3897,9 +3743,6 @@ func newServer(ctx context.Context, listenAddrs []string, db database.DB, chainP
 		if s.addrIndex != nil {
 			rpcsConfig.AddrIndexer = s.addrIndex
 		}
-		if s.cfIndex != nil {
-			rpcsConfig.Filterer = s.cfIndex
-		}
 
 		s.rpcServer, err = rpcserver.New(&rpcsConfig)
 		if err != nil {


Test patch:
diff --git a/blockchain/agendas_test.go b/blockchain/agendas_test.go
index 82bb19f3..706590f5 100644
--- a/blockchain/agendas_test.go
+++ b/blockchain/agendas_test.go
@@ -170,173 +170,28 @@ func TestLNFeaturesDeployment(t *testing.T) {
 	testLNFeaturesDeployment(t, chaincfg.RegNetParams())
 }
 
-// testFixSeqLocksDeployment ensures the deployment of the fix sequence locks
-// agenda activates for the provided network parameters.
-func testFixSeqLocksDeployment(t *testing.T, params *chaincfg.Params) {
-	// Clone the parameters so they can be mutated, find the correct deployment
-	// for the fix sequence locks agenda as well as the yes vote choice within
-	// it, and, finally, ensure it is always available to vote by removing the
-	// time constraints to prevent test failures when the real expiration time
-	// passes.
-	params = cloneParams(params)
-	deploymentVer, deployment, err := findDeployment(params,
-		chaincfg.VoteIDFixLNSeqLocks)
-	if err != nil {
-		t.Fatal(err)
-	}
-	yesChoice, err := findDeploymentChoice(deployment, "yes")
-	if err != nil {
-		t.Fatal(err)
-	}
-	removeDeploymentTimeConstraints(deployment)
-
-	// Shorter versions of params for convenience.
-	stakeValidationHeight := uint32(params.StakeValidationHeight)
-	ruleChangeActivationInterval := params.RuleChangeActivationInterval
-
-	tests := []struct {
-		name       string
-		numNodes   uint32 // num fake nodes to create
-		curActive  bool   // whether agenda active for current block
-		nextActive bool   // whether agenda active for NEXT block
-	}{
-		{
-			name:       "stake validation height",
-			numNodes:   stakeValidationHeight,
-			curActive:  false,
-			nextActive: false,
-		},
-		{
-			name:       "started",
-			numNodes:   ruleChangeActivationInterval,
-			curActive:  false,
-			nextActive: false,
-		},
-		{
-			name:       "lockedin",
-			numNodes:   ruleChangeActivationInterval,
-			curActive:  false,
-			nextActive: false,
-		},
-		{
-			name:       "one before active",
-			numNodes:   ruleChangeActivationInterval - 1,
-			curActive:  false,
-			nextActive: true,
-		},
-		{
-			name:       "exactly active",
-			numNodes:   1,
-			curActive:  true,
-			nextActive: true,
-		},
-		{
-			name:       "one after active",
-			numNodes:   1,
-			curActive:  true,
-			nextActive: true,
-		},
-	}
-
-	curTimestamp := time.Now()
-	bc := newFakeChain(params)
-	node := bc.bestChain.Tip()
-	for _, test := range tests {
-		for i := uint32(0); i < test.numNodes; i++ {
-			node = newFakeNode(node, int32(deploymentVer), deploymentVer, 0,
-				curTimestamp)
-
-			// Create fake votes that vote yes on the agenda to ensure it is
-			// activated.
-			for j := uint16(0); j < params.TicketsPerBlock; j++ {
-				node.votes = append(node.votes, stake.VoteVersionTuple{
-					Version: deploymentVer,
-					Bits:    yesChoice.Bits | 0x01,
-				})
-			}
-			bc.bestChain.SetTip(node)
-			curTimestamp = curTimestamp.Add(time.Second)
-		}
-
-		// Ensure the agenda reports the expected activation status for the
-		// current block.
-		gotActive, err := bc.isFixSeqLocksAgendaActive(node.parent)
-		if err != nil {
-			t.Errorf("%s: unexpected err: %v", test.name, err)
-			continue
-		}
-		if gotActive != test.curActive {
-			t.Errorf("%s: mismatched current active status - got: %v, want: %v",
-				test.name, gotActive, test.curActive)
-			continue
-		}
-
-		// Ensure the agenda reports the expected activation status for the NEXT
-		// block
-		gotActive, err = bc.IsFixSeqLocksAgendaActive()
-		if err != nil {
-			t.Errorf("%s: unexpected err: %v", test.name, err)
-			continue
-		}
-		if gotActive != test.nextActive {
-			t.Errorf("%s: mismatched next active status - got: %v, want: %v",
-				test.name, gotActive, test.nextActive)
-			continue
-		}
-	}
-}
-
-// TestFixSeqLocksDeployment ensures the deployment of the fix sequence locks
-// agenda activates as expected.
-func TestFixSeqLocksDeployment(t *testing.T) {
-	testFixSeqLocksDeployment(t, chaincfg.MainNetParams())
-	testFixSeqLocksDeployment(t, chaincfg.RegNetParams())
-}
-
 // TestFixedSequenceLocks ensures that sequence locks within blocks behave as
-// expected once the fix sequence locks agenda is active.
+// expected.
 func TestFixedSequenceLocks(t *testing.T) {
 	// Use a set of test chain parameters which allow for quicker vote
 	// activation as compared to various existing network params.
 	params := quickVoteActivationParams()
 
-	// Clone the parameters so they can be mutated, find the correct deployment
-	// for the fix sequence locks agenda, and, finally, ensure it is always
-	// available to vote by removing the time constraints to prevent test
-	// failures when the real expiration time passes.
-	const fslVoteID = chaincfg.VoteIDFixLNSeqLocks
+	// Clone the parameters so they can be mutated.
 	params = cloneParams(params)
-	fslVersion, deployment, err := findDeployment(params, fslVoteID)
-	if err != nil {
-		t.Fatal(err)
-	}
-	removeDeploymentTimeConstraints(deployment)
 
 	// Create a test harness initialized with the genesis block as the tip.
 	g, teardownFunc := newChaingenHarness(t, params, "fixseqlockstest")
 	defer teardownFunc()
 
-	// replaceFixSeqLocksVersions is a munge function which modifies the
-	// provided block by replacing the block, stake, and vote versions with the
-	// fix sequence locks deployment version.
-	replaceFixSeqLocksVersions := func(b *wire.MsgBlock) {
-		chaingen.ReplaceBlockVersion(int32(fslVersion))(b)
-		chaingen.ReplaceStakeVersion(fslVersion)(b)
-		chaingen.ReplaceVoteVersions(fslVersion)(b)
-	}
-
 	// ---------------------------------------------------------------------
-	// Generate and accept enough blocks with the appropriate vote bits set
-	// to reach one block prior to the fix sequence locks agenda becoming
-	// active.
+	// Generate and accept enough blocks to reach stake validation height.
 	// ---------------------------------------------------------------------
 
 	g.AdvanceToStakeValidationHeight()
-	g.AdvanceFromSVHToActiveAgenda(fslVoteID)
 
 	// ---------------------------------------------------------------------
-	// Perform a series of sequence lock tests now that fix sequence locks
-	// enforcement is active.
+	// Perform a series of sequence lock tests.
 	// ---------------------------------------------------------------------
 
 	// enableSeqLocks modifies the passed transaction to enable sequence locks
@@ -356,29 +211,28 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs := g.OldestCoinbaseOuts()
-	b0 := g.NextBlock("b0", &outs[0], outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			// Save the current outputs of the spend tx and clear them.
-			tx := b.Transactions[1]
-			origOut := tx.TxOut[0]
-			origOpReturnOut := tx.TxOut[1]
-			tx.TxOut = tx.TxOut[:0]
-
-			// Evenly split the original output amount over multiple outputs.
-			const numOutputs = 6
-			amount := origOut.Value / numOutputs
-			for i := 0; i < numOutputs; i++ {
-				if i == numOutputs-1 {
-					amount = origOut.Value - amount*(numOutputs-1)
-				}
-				tx.AddTxOut(wire.NewTxOut(amount, origOut.PkScript))
+	b0 := g.NextBlock("b0", &outs[0], outs[1:], func(b *wire.MsgBlock) {
+		// Save the current outputs of the spend tx and clear them.
+		tx := b.Transactions[1]
+		origOut := tx.TxOut[0]
+		origOpReturnOut := tx.TxOut[1]
+		tx.TxOut = tx.TxOut[:0]
+
+		// Evenly split the original output amount over multiple outputs.
+		const numOutputs = 6
+		amount := origOut.Value / numOutputs
+		for i := 0; i < numOutputs; i++ {
+			if i == numOutputs-1 {
+				amount = origOut.Value - amount*(numOutputs-1)
 			}
+			tx.AddTxOut(wire.NewTxOut(amount, origOut.PkScript))
+		}
 
-			// Add the original op return back to the outputs and enable
-			// sequence locks for the first output.
-			tx.AddTxOut(origOpReturnOut)
-			enableSeqLocks(tx, 0)
-		})
+		// Add the original op return back to the outputs and enable
+		// sequence locks for the first output.
+		tx.AddTxOut(origOpReturnOut)
+		enableSeqLocks(tx, 0)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
@@ -390,13 +244,12 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b1a", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 0)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b1a", nil, outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b0, 1, 0)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		enableSeqLocks(tx, 0)
+		b.AddTransaction(tx)
+	})
 	g.AcceptTipBlock()
 
 	// ---------------------------------------------------------------------
@@ -408,18 +261,17 @@ func TestFixedSequenceLocks(t *testing.T) {
 	//            \-> b1a
 	// ---------------------------------------------------------------------
 	g.SetTip("b0")
-	g.NextBlock("b1", nil, outs[1:], replaceFixSeqLocksVersions)
+	g.NextBlock("b1", nil, outs[1:])
 	g.SaveTipCoinbaseOuts()
 	g.AcceptedToSideChainWithExpectedTip("b1a")
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b2", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 0)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b2", nil, outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b0, 1, 0)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		enableSeqLocks(tx, 0)
+		b.AddTransaction(tx)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 	g.ExpectTip("b2")
@@ -431,10 +283,9 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b3", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			enableSeqLocks(b.STransactions[0], 0)
-		})
+	g.NextBlock("b3", nil, outs[1:], func(b *wire.MsgBlock) {
+		enableSeqLocks(b.STransactions[0], 0)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
@@ -445,10 +296,9 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b4", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			enableSeqLocks(b.STransactions[5], 0)
-		})
+	g.NextBlock("b4", nil, outs[1:], func(b *wire.MsgBlock) {
+		enableSeqLocks(b.STransactions[5], 0)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
@@ -461,23 +311,21 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b5", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 1)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b5", nil, outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b0, 1, 1)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		b.AddTransaction(tx)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b6", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 2)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b6", nil, outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b0, 1, 2)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		enableSeqLocks(tx, 0)
+		b.AddTransaction(tx)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
@@ -491,13 +339,12 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b7", &outs[0], outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b, 1, 0)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b7", &outs[0], outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b, 1, 0)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		enableSeqLocks(tx, 0)
+		b.AddTransaction(tx)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
@@ -511,18 +358,17 @@ func TestFixedSequenceLocks(t *testing.T) {
 	// ---------------------------------------------------------------------
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b8", nil, outs[1:], replaceFixSeqLocksVersions)
+	g.NextBlock("b8", nil, outs[1:])
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b9", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 3)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b9", nil, outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b0, 1, 3)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		enableSeqLocks(tx, 0)
+		b.AddTransaction(tx)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
@@ -544,18 +390,17 @@ func TestFixedSequenceLocks(t *testing.T) {
 	)
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b10", nil, outs[1:], replaceFixSeqLocksVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 4)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			b.AddTransaction(tx)
-		})
+	g.NextBlock("b10", nil, outs[1:], func(b *wire.MsgBlock) {
+		spend := chaingen.MakeSpendableOut(b0, 1, 4)
+		tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
+		b.AddTransaction(tx)
+	})
 	g.SaveTipCoinbaseOuts()
 	g.AcceptTipBlock()
 
 	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b11", nil, outs[1:], replaceFixSeqLocksVersions,
-		chaingen.ReplaceVotes(vbDisapprovePrev, fslVersion),
+	g.NextBlock("b11", nil, outs[1:],
+		g.ReplaceVoteBits(vbDisapprovePrev),
 		func(b *wire.MsgBlock) {
 			b.Header.VoteBits &^= vbApprovePrev
 			spend := chaingen.MakeSpendableOut(b0, 1, 5)
diff --git a/blockchain/validate_test.go b/blockchain/validate_test.go
index 27517d95..6154b252 100644
--- a/blockchain/validate_test.go
+++ b/blockchain/validate_test.go
@@ -256,276 +256,6 @@ func quickVoteActivationParams() *chaincfg.Params {
 	return params
 }
 
-// TestLegacySequenceLocks ensure that sequence locks within blocks behave as
-// expected according to the legacy semantics in previous version of the
-// software.
-func TestLegacySequenceLocks(t *testing.T) {
-	// Use a set of test chain parameters which allow for quicker vote
-	// activation as compared to various existing network params.
-	params := quickVoteActivationParams()
-
-	// Clone the parameters so they can be mutated, find the correct deployment
-	// for the LN features agenda, and, finally, ensure it is always available
-	// to vote by removing the time constraints to prevent test failures when
-	// the real expiration time passes.
-	const lnfVoteID = chaincfg.VoteIDLNFeatures
-	params = cloneParams(params)
-	lnfVersion, deployment, err := findDeployment(params, lnfVoteID)
-	if err != nil {
-		t.Fatal(err)
-	}
-	removeDeploymentTimeConstraints(deployment)
-
-	// Create a test harness initialized with the genesis block as the tip.
-	g, teardownFunc := newChaingenHarness(t, params, "seqlocksoldsemanticstest")
-	defer teardownFunc()
-
-	// replaceLNFeaturesVersions is a munge function which modifies the provided
-	// block by replacing the block, stake, and vote versions with the LN
-	// features deployment version.
-	replaceLNFeaturesVersions := func(b *wire.MsgBlock) {
-		chaingen.ReplaceBlockVersion(int32(lnfVersion))(b)
-		chaingen.ReplaceStakeVersion(lnfVersion)(b)
-		chaingen.ReplaceVoteVersions(lnfVersion)(b)
-	}
-
-	// ---------------------------------------------------------------------
-	// Generate and accept enough blocks with the appropriate vote bits set
-	// to reach one block prior to the LN features agenda becoming active.
-	// ---------------------------------------------------------------------
-
-	g.AdvanceToStakeValidationHeight()
-	g.AdvanceFromSVHToActiveAgenda(lnfVoteID)
-
-	// ---------------------------------------------------------------------
-	// Perform a series of sequence lock tests now that ln feature
-	// enforcement is active.
-	// ---------------------------------------------------------------------
-
-	// enableSeqLocks modifies the passed transaction to enable sequence locks
-	// for the provided input.
-	enableSeqLocks := func(tx *wire.MsgTx, txInIdx int) {
-		tx.Version = 2
-		tx.TxIn[txInIdx].Sequence = 0
-	}
-
-	// ---------------------------------------------------------------------
-	// Create block that has a transaction with an input shared with a
-	// transaction in the stake tree and has several outputs used in
-	// subsequent blocks.  Also, enable sequence locks for the first of
-	// those outputs.
-	//
-	//   ... -> b0
-	// ---------------------------------------------------------------------
-
-	outs := g.OldestCoinbaseOuts()
-	b0 := g.NextBlock("b0", &outs[0], outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			// Save the current outputs of the spend tx and clear them.
-			tx := b.Transactions[1]
-			origOut := tx.TxOut[0]
-			origOpReturnOut := tx.TxOut[1]
-			tx.TxOut = tx.TxOut[:0]
-
-			// Evenly split the original output amount over multiple outputs.
-			const numOutputs = 6
-			amount := origOut.Value / numOutputs
-			for i := 0; i < numOutputs; i++ {
-				if i == numOutputs-1 {
-					amount = origOut.Value - amount*(numOutputs-1)
-				}
-				tx.AddTxOut(wire.NewTxOut(amount, origOut.PkScript))
-			}
-
-			// Add the original op return back to the outputs and enable
-			// sequence locks for the first output.
-			tx.AddTxOut(origOpReturnOut)
-			enableSeqLocks(tx, 0)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	// ---------------------------------------------------------------------
-	// Create block that spends from an output created in the previous
-	// block.
-	//
-	//   ... -> b0 -> b1a
-	// ---------------------------------------------------------------------
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b1a", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 0)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
-	g.AcceptTipBlock()
-
-	// ---------------------------------------------------------------------
-	// Create block that involves reorganize to a sequence lock spending
-	// from an output created in a block prior to the parent also spent on
-	// on the side chain.
-	//
-	//   ... -> b0 -> b1  -> b2
-	//            \-> b1a
-	// ---------------------------------------------------------------------
-	g.SetTip("b0")
-	g.NextBlock("b1", nil, outs[1:], replaceLNFeaturesVersions)
-	g.SaveTipCoinbaseOuts()
-	g.AcceptedToSideChainWithExpectedTip("b1a")
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b2", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 0)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-	g.ExpectTip("b2")
-
-	// ---------------------------------------------------------------------
-	// Create block that involves a sequence lock on a vote.
-	//
-	//   ... -> b2 -> b3
-	// ---------------------------------------------------------------------
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b3", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			enableSeqLocks(b.STransactions[0], 0)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	// ---------------------------------------------------------------------
-	// Create block that involves a sequence lock on a ticket.
-	//
-	//   ... -> b3 -> b4
-	// ---------------------------------------------------------------------
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b4", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			enableSeqLocks(b.STransactions[5], 0)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	// ---------------------------------------------------------------------
-	// Create two blocks such that the tip block involves a sequence lock
-	// spending from a different output of a transaction the parent block
-	// also spends from.
-	//
-	//   ... -> b4 -> b5 -> b6
-	// ---------------------------------------------------------------------
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b5", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 1)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			b.AddTransaction(tx)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b6", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 2)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	// ---------------------------------------------------------------------
-	// Create block that involves a sequence lock spending from a regular
-	// tree transaction earlier in the block.  It should be rejected due
-	// to a consensus bug.
-	//
-	//   ... -> b6
-	//            \-> b7
-	// ---------------------------------------------------------------------
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b7", &outs[0], outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b, 1, 0)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
-	g.RejectTipBlock(ErrMissingTxOut)
-
-	// ---------------------------------------------------------------------
-	// Create block that involves a sequence lock spending from a block
-	// prior to the parent.  It should be rejected due to a consensus bug.
-	//
-	//   ... -> b6 -> b8
-	//                  \-> b9
-	// ---------------------------------------------------------------------
-
-	g.SetTip("b6")
-	g.NextBlock("b8", nil, outs[1:], replaceLNFeaturesVersions)
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b9", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 3)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
-	g.RejectTipBlock(ErrMissingTxOut)
-
-	// ---------------------------------------------------------------------
-	// Create two blocks such that the tip block involves a sequence lock
-	// spending from a different output of a transaction the parent block
-	// also spends from when the parent block has been disapproved.    It
-	// should be rejected due to a consensus bug.
-	//
-	//   ... -> b8 -> b10
-	//                   \-> b11
-	// ---------------------------------------------------------------------
-
-	const (
-		// vbDisapprovePrev and vbApprovePrev represent no and yes votes,
-		// respectively, on whether or not to approve the previous block.
-		vbDisapprovePrev = 0x0000
-		vbApprovePrev    = 0x0001
-	)
-
-	g.SetTip("b8")
-	g.NextBlock("b10", nil, outs[1:], replaceLNFeaturesVersions,
-		func(b *wire.MsgBlock) {
-			spend := chaingen.MakeSpendableOut(b0, 1, 4)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			b.AddTransaction(tx)
-		})
-	g.SaveTipCoinbaseOuts()
-	g.AcceptTipBlock()
-
-	outs = g.OldestCoinbaseOuts()
-	g.NextBlock("b11", nil, outs[1:], replaceLNFeaturesVersions,
-		chaingen.ReplaceVotes(vbDisapprovePrev, lnfVersion),
-		func(b *wire.MsgBlock) {
-			b.Header.VoteBits &^= vbApprovePrev
-			spend := chaingen.MakeSpendableOut(b0, 1, 5)
-			tx := g.CreateSpendTx(&spend, dcrutil.Amount(1))
-			enableSeqLocks(tx, 0)
-			b.AddTransaction(tx)
-		})
-	g.RejectTipBlock(ErrMissingTxOut)
-}
-
 // TestCheckBlockSanity tests the context free block sanity checks with blocks
 // not on a chain.
 func TestCheckBlockSanity(t *testing.T) {
diff --git a/chaincfg/testnetparams.go b/chaincfg/testnetparams.go
index 74fae2c0..e49f2159 100644
--- a/chaincfg/testnetparams.go
+++ b/chaincfg/testnetparams.go
@@ -306,6 +306,9 @@ func TestNet3Params() *Params {
 		seeders: []string{
 			"testnet-seed-1.decred.org",
 			"testnet-seed-2.decred.org",
+			"testnet-seed.planetdecred.org",
+			"testnet-seed.dcrdata.org",
+			"testnet-seed.jamieholdstock.com",
 		},
 	}
 }
diff --git a/gcs/blockcf/bench_test.go b/gcs/blockcf/bench_test.go
deleted file mode 100644
index e12d316e..00000000
--- a/gcs/blockcf/bench_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2019 The Decred developers
-// Use of this source code is governed by an ISC
-// license that can be found in the LICENSE file.
-
-package blockcf
-
-import (
-	"encoding/hex"
-	"testing"
-)
-
-// BenchmarkAddSigScript benchmarks adding the data pushes in signature scripts
-// to a slice of entries that are ultimately included in a filter.
-func BenchmarkAddSigScript(b *testing.B) {
-	// Use a standard v0 redeem P2PKH signature script.
-	sigScriptHex := "483045022100c5df7450e24ba20b0a4f9fa68abe22bd7e67cdeab3ef" +
-		"878583a418a10196d909022022ed41a867f6de40601d58679898d0d855a003f42a31" +
-		"16bb70938a5a2b1345110121020e0a918baf432ae23d815e4572cc71f9739a5516f9" +
-		"70cf1236fa529ee8bd3343"
-	sigScript, err := hex.DecodeString(sigScriptHex)
-	if err != nil {
-		b.Fatalf("failed to initialize input data: %v", err)
-	}
-
-	b.ResetTimer()
-	for i := 0; i < b.N; i++ {
-		var data Entries
-		data.AddSigScript(sigScript)
-	}
-}
diff --git a/internal/rpcserver/rpcserverhandlers_test.go b/internal/rpcserver/rpcserverhandlers_test.go
index e4151d5b..65d0317b 100644
--- a/internal/rpcserver/rpcserverhandlers_test.go
+++ b/internal/rpcserver/rpcserverhandlers_test.go
@@ -923,26 +923,6 @@ func (s *testSanityChecker) CheckBlockSanity(block *dcrutil.Block) error {
 	return s.checkBlockSanityErr
 }
 
-// testFilterer provides a mock filterer by implementing the Filterer interface.
-type testFilterer struct {
-	filterByBlockHash          []byte
-	filterByBlockHashErr       error
-	filterHeaderByBlockHash    []byte
-	filterHeaderByBlockHashErr error
-}
-
-// FilterByBlockHash returns a mocked regular or extended committed filter for
-// the given block hash.
-func (f *testFilterer) FilterByBlockHash(h *chainhash.Hash, filterType wire.FilterType) ([]byte, error) {
-	return f.filterByBlockHash, f.filterByBlockHashErr
-}
-
-// FilterHeaderByBlockHash returns a mocked regular or extended committed filter
-// header for the given block hash.
-func (f *testFilterer) FilterHeaderByBlockHash(h *chainhash.Hash, filterType wire.FilterType) ([]byte, error) {
-	return f.filterHeaderByBlockHash, f.filterHeaderByBlockHashErr
-}
-
 // testFiltererV2 provides a mock V2 filterer by implementing the FiltererV2
 // interface.
 type testFiltererV2 struct {
@@ -1315,7 +1295,6 @@ type rpcTest struct {
 	mockConnManager       *testConnManager
 	mockClock             *testClock
 	mockLogManager        *testLogManager
-	mockFilterer          *testFilterer
 	mockFiltererV2        *testFiltererV2
 	mockTxMempooler       *testTxMempooler
 	mockMiningAddrs       []dcrutil.Address
@@ -2704,7 +2683,9 @@ func TestHandleEstimateSmartFee(t *testing.T) {
 	economical := types.EstimateSmartFeeEconomical
 	validFeeEstimator := defaultMockFeeEstimator()
 	validFeeEstimator.estimateFeeAmt = 123456789
-	validFee := float64(1.23456789)
+	result := &types.EstimateSmartFeeResult{
+		FeeRate: float64(1.23456789),
+	}
 	testRPCServerHandler(t, []rpcTest{{
 		name:    "handleEstimateSmartFee: ok with mode",
 		handler: handleEstimateSmartFee,
@@ -2713,13 +2694,13 @@ func TestHandleEstimateSmartFee(t *testing.T) {
 			Mode:          &conservative,
 		},
 		mockFeeEstimator: validFeeEstimator,
-		result:           validFee,
+		result:           result,
 	}, {
 		name:             "handleEstimateSmartFee: ok no mode",
 		handler:          handleEstimateSmartFee,
 		cmd:              &types.EstimateSmartFeeCmd{},
 		mockFeeEstimator: validFeeEstimator,
-		result:           validFee,
+		result:           result,
 	}, {
 		name:    "handleEstimateSmartFee: not conservative mode",
 		handler: handleEstimateSmartFee,
@@ -4039,205 +4020,6 @@ func TestHandleGetBlockSubsidy(t *testing.T) {
 	}})
 }
 
-func TestHandleGetCFilter(t *testing.T) {
-	t.Parallel()
-
-	blkHashString := block432100.BlockHash().String()
-	filterString := "000000112f2f7a8d1b6dd2170afadb2d5f9ae11085487a270d07ccdfb5e" +
-		"31796a6162379f000001d43f94851308c4a1534d8"
-	filter := hexToBytes(filterString)
-	extendedFilterString := "0000000dbd0b842aa0b9bbe14fb5df84cbb2143c32ba4710767" +
-		"02fbf52af819387b8216504c8d1"
-	extendedFilter := hexToBytes(extendedFilterString)
-	testRPCServerHandler(t, []rpcTest{{
-		name:    "handleGetCFilter: ok regular",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterByBlockHash: filter,
-		},
-		result: filterString,
-	}, {
-		name:    "handleGetCFilter: ok extended",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       blkHashString,
-			FilterType: "extended",
-		},
-		mockFilterer: &testFilterer{
-			filterByBlockHash: extendedFilter,
-		},
-		result: extendedFilterString,
-	}, {
-		name:    "handleGetCFilter: compact filters not enabled",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCNoCFIndex,
-	}, {
-		name:    "handleGetCFilter: invalid hash",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       "invalid",
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterByBlockHash: filter,
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCDecodeHexString,
-	}, {
-		name:    "handleGetCFilter: unknown filter type",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       blkHashString,
-			FilterType: "unknown",
-		},
-		mockFilterer: &testFilterer{
-			filterByBlockHash: filter,
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCInvalidParameter,
-	}, {
-		name:    "handleGetCFilter: failed to load filter",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterByBlockHashErr: errors.New("failed to load filter"),
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCInternal.Code,
-	}, {
-		name:    "handleGetCFilter: block not found",
-		handler: handleGetCFilter,
-		cmd: &types.GetCFilterCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{},
-		wantErr:      true,
-		errCode:      dcrjson.ErrRPCBlockNotFound,
-	}})
-}
-
-func TestHandleGetCFilterHeader(t *testing.T) {
-	t.Parallel()
-
-	blkHashString := block432100.BlockHash().String()
-	filter := hexToBytes("bd60b439d72fad354acc4b47d374ec34b0cca3bb25994b459f01f6" +
-		"f84ad6b478")
-	filterHash, err := chainhash.NewHash(filter)
-	if err != nil {
-		t.Fatalf("error creating chainhash.Hash: %+v", err)
-	}
-	extendedFilter := hexToBytes("72109550799eb4cedaec527fea34a3a2fb42f1e9187da1" +
-		"bac1fd8d66274222bd")
-	extendedFilterHash, err := chainhash.NewHash(extendedFilter)
-	if err != nil {
-		t.Fatalf("error creating chainhash.Hash: %+v", err)
-	}
-	testRPCServerHandler(t, []rpcTest{{
-		name:    "handleGetCFilterHeader: ok regular",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHash: filter,
-		},
-		result: filterHash.String(),
-	}, {
-		name:    "handleGetCFilterHeader: ok extended",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       blkHashString,
-			FilterType: "extended",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHash: extendedFilter,
-		},
-		result: extendedFilterHash.String(),
-	}, {
-		name:    "handleGetCFilterHeader: ok genesis block",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       defaultChainParams.GenesisHash.String(),
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHash: zeroHash[:],
-		},
-		result: zeroHash.String(),
-	}, {
-		name:    "handleGetCFilterHeader: compact filters not enabled",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCNoCFIndex,
-	}, {
-		name:    "handleGetCFilterHeader: invalid hash",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       "invalid",
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHash: filter,
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCDecodeHexString,
-	}, {
-		name:    "handleGetCFilterHeader: unknown filter type",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       blkHashString,
-			FilterType: "unknown",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHash: filter,
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCInvalidParameter,
-	}, {
-		name:    "handleGetCFilterHeader: failed to load filter",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHashErr: errors.New("failed to load filter"),
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCInternal.Code,
-	}, {
-		name:    "handleGetCFilterHeader: block not found",
-		handler: handleGetCFilterHeader,
-		cmd: &types.GetCFilterHeaderCmd{
-			Hash:       blkHashString,
-			FilterType: "regular",
-		},
-		mockFilterer: &testFilterer{
-			filterHeaderByBlockHash: zeroHash[:],
-		},
-		wantErr: true,
-		errCode: dcrjson.ErrRPCBlockNotFound,
-	}})
-}
-
 func TestHandleGetCFilterV2(t *testing.T) {
 	t.Parallel()
 
@@ -7955,9 +7737,6 @@ func testRPCServerHandler(t *testing.T, tests []rpcTest) {
 			if test.mockSanityChecker != nil {
 				rpcserverConfig.SanityChecker = test.mockSanityChecker
 			}
-			if test.mockFilterer != nil {
-				rpcserverConfig.Filterer = test.mockFilterer
-			}
 			if test.mockFiltererV2 != nil {
 				rpcserverConfig.FiltererV2 = test.mockFiltererV2
 			}
diff --git a/rpc/jsonrpc/types/chainsvrcmds_test.go b/rpc/jsonrpc/types/chainsvrcmds_test.go
index eb709f2f..fe85b142 100644
--- a/rpc/jsonrpc/types/chainsvrcmds_test.go
+++ b/rpc/jsonrpc/types/chainsvrcmds_test.go
@@ -341,34 +341,6 @@ func TestChainSvrCmds(t *testing.T) {
 				Voters: 256,
 			},
 		},
-		{
-			name: "getcfilter",
-			newCmd: func() (interface{}, error) {
-				return dcrjson.NewCmd(Method("getcfilter"), "123", "extended")
-			},
-			staticCmd: func() interface{} {
-				return NewGetCFilterCmd("123", "extended")
-			},
-			marshalled: `{"jsonrpc":"1.0","method":"getcfilter","params":["123","extended"],"id":1}`,
-			unmarshalled: &GetCFilterCmd{
-				Hash:       "123",
-				FilterType: "extended",
-			},
-		},
-		{
-			name: "getcfilterheader",
-			newCmd: func() (interface{}, error) {
-				return dcrjson.NewCmd(Method("getcfilterheader"), "123", "extended")
-			},
-			staticCmd: func() interface{} {
-				return NewGetCFilterHeaderCmd("123", "extended")
-			},
-			marshalled: `{"jsonrpc":"1.0","method":"getcfilterheader","params":["123","extended"],"id":1}`,
-			unmarshalled: &GetCFilterHeaderCmd{
-				Hash:       "123",
-				FilterType: "extended",
-			},
-		},
 		{
 			name: "getcfilterv2",
 			newCmd: func() (interface{}, error) {