diff --git a/blockchain/indexers/cfindex.go b/blockchain/indexers/cfindex.go index 482c24e1..04737679 100644 --- a/blockchain/indexers/cfindex.go +++ b/blockchain/indexers/cfindex.go @@ -5,15 +5,11 @@ package indexers import ( - "fmt" - "github.com/btcsuite/btcd/blockchain" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/gcs/builder" - - "os" ) const ( @@ -21,67 +17,86 @@ const ( cfIndexName = "committed filter index" ) +// Committed filters come in two flavours: 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 ( // cfBasicIndexKey is the name of the db bucket used to house the - // block hash -> Basic CF index (CF #0). + // block hash -> Basic cf index (cf#0). cfBasicIndexKey = []byte("cf0byhashidx") // cfExtendedIndexKey is the name of the db bucket used to house the - // block hash -> Extended CF index (CF #1). + // block hash -> Extended cf index (cf#1). cfExtendedIndexKey = []byte("cf1byhashidx") ) -func dbFetchCFIndexEntry(dbTx database.Tx, blockHash *chainhash.Hash) ([]byte, - error) { - // Load the record from the database and return now if it doesn't exist. - index := dbTx.Metadata().Bucket(cfBasicIndexKey) - serializedFilter := index.Get(blockHash[:]) - if len(serializedFilter) == 0 { - return nil, nil - } - - return serializedFilter, nil +// dbFetchBasicEntry() retrieves a block's basic filter. An entry's absence is +// not considered an error. The filter is returned serialized. +func dbFetchBasicEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) { + idx := dbTx.Metadata().Bucket(cfBasicIndexKey) + return idx.Get(h[:]), nil } -// The serialized format for keys and values in the block hash to CF bucket is: -// = -// -// Field Type Size -// hash chainhash.Hash 32 bytes -// CF []byte variable -// ----- -// Total: > 32 bytes +// dbFetchExtendedEntry() retrieves a block's extended filter. An entry's +// absence is not considered an error. The filter is returned serialized. +func dbFetchExtendedEntry(dbTx database.Tx, h *chainhash.Hash) ([]byte, error) { + idx := dbTx.Metadata().Bucket(cfExtendedIndexKey) + return idx.Get(h[:]), nil +} -// CFIndex implements a CF by hash index. -type CFIndex struct { +// dbStoreBasicEntry() stores a block's basic filter. +func dbStoreBasicEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error { + idx := dbTx.Metadata().Bucket(cfBasicIndexKey) + return idx.Put(h[:], f) +} + +// dbStoreBasicEntry() stores a block's extended filter. +func dbStoreExtendedEntry(dbTx database.Tx, h *chainhash.Hash, f []byte) error { + idx := dbTx.Metadata().Bucket(cfExtendedIndexKey) + return idx.Put(h[:], f) +} + +// dbDeleteBasicEntry() deletes a block's basic filter. +func dbDeleteBasicEntry(dbTx database.Tx, h *chainhash.Hash) error { + idx := dbTx.Metadata().Bucket(cfBasicIndexKey) + return idx.Delete(h[:]) +} + +// dbDeleteExtendedEntry() deletes a block's extended filter. +func dbDeleteExtendedEntry(dbTx database.Tx, h *chainhash.Hash) error { + idx := dbTx.Metadata().Bucket(cfExtendedIndexKey) + return idx.Delete(h[:]) +} + +// CfIndex implements a committed filter (cf) by hash index. +type CfIndex struct { db database.DB } -// Ensure the CFIndex type implements the Indexer interface. -var _ Indexer = (*CFIndex)(nil) +// 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 +// 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. -func (idx *CFIndex) Key() []byte { +// 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 cfBasicIndexKey } -// Name returns the human-readable name of the index. -// -// This is part of the Indexer interface. -func (idx *CFIndex) Name() string { +// Name returns the human-readable name of the index. This is part of the +// Indexer interface. +func (idx *CfIndex) Name() string { return cfIndexName } // 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 +// 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 { +func (idx *CfIndex) Create(dbTx database.Tx) error { meta := dbTx.Metadata() _, err := meta.CreateBucket(cfBasicIndexKey) if err != nil { @@ -91,7 +106,9 @@ func (idx *CFIndex) Create(dbTx database.Tx) error { return err } -func generateFilterForBlock(block *btcutil.Block) ([]byte, error) { +// makeBasicFilter() builds a block's basic filter, which consists of all +// outpoints referenced by transactions within the block. +func makeBasicFilterForBlock(block *btcutil.Block) ([]byte, error) { b := builder.WithKeyHash(block.Hash()) _, err := b.Key() if err != nil { @@ -110,67 +127,47 @@ func generateFilterForBlock(block *btcutil.Block) ([]byte, error) { } // 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 *btcutil.Block, +// 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 *btcutil.Block, view *blockchain.UtxoViewpoint) error { - filterBytes, err := generateFilterForBlock(block) + f, err := makeBasicFilterForBlock(block) if err != nil { return err } - - meta := dbTx.Metadata() - index := meta.Bucket(cfBasicIndexKey) - err = index.Put(block.Hash()[:], filterBytes) - if err != nil { - return err - } - - fmt.Fprintf(os.Stderr, "Stored CF for block %v", block.Hash()) - - return nil + return dbStoreBasicEntry(dbTx, block.Hash(), f) } // 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 *btcutil.Block, +// 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 *btcutil.Block, view *blockchain.UtxoViewpoint) error { - index := dbTx.Metadata().Bucket(cfBasicIndexKey) - filterBytes := index.Get(block.Hash()[:]) - if len(filterBytes) == 0 { - return fmt.Errorf("can't remove non-existent filter %s from " + - "the cfilter index", block.Hash()) - } - return index.Delete(block.Hash()[:]) + return dbDeleteBasicEntry(dbTx, block.Hash()) } -func (idx *CFIndex) FilterByBlockHash(hash *chainhash.Hash) ([]byte, error) { +func (idx *CfIndex) FilterByBlockHash(hash *chainhash.Hash) ([]byte, error) { var filterBytes []byte err := idx.db.View(func(dbTx database.Tx) error { var err error - filterBytes, err = dbFetchCFIndexEntry(dbTx, hash) + filterBytes, err = dbFetchBasicEntry(dbTx, hash) return err }) return filterBytes, err } -// NewCFIndex returns a new instance of an indexer that is used to create a +// 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 bloom filters. +// 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 +// turn is used by the blockchain package. This allows the index to be // seamlessly maintained along with the chain. -func NewCFIndex(db database.DB) *CFIndex { - return &CFIndex{db: db} +func NewCfIndex(db database.DB) *CfIndex { + return &CfIndex{db: db} } -// DropCFIndex drops the CF index from the provided database if exists. -func DropCFIndex(db database.DB) error { +// DropCfIndex drops the CF index from the provided database if exists. +func DropCfIndex(db database.DB) error { return dropIndex(db, cfBasicIndexKey, cfIndexName) } diff --git a/server.go b/server.go index 7ece52eb..bcfcf752 100644 --- a/server.go +++ b/server.go @@ -229,7 +229,7 @@ type server struct { // do not need to be protected for concurrent access. txIndex *indexers.TxIndex addrIndex *indexers.AddrIndex - cfIndex *indexers.CFIndex + cfIndex *indexers.CfIndex } // serverPeer extends the peer to maintain state shared by the server and @@ -2244,8 +2244,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param indexes = append(indexes, s.addrIndex) } if !cfg.NoCFilters { - indxLog.Info("CF index is enabled") - s.cfIndex = indexers.NewCFIndex(db) + indxLog.Info("cf index is enabled") + s.cfIndex = indexers.NewCfIndex(db) indexes = append(indexes, s.cfIndex) }