CBFilter -> CFilter, discussed with davec@

This commit is contained in:
pedro martelletto 2017-01-18 08:09:05 +00:00 committed by Olaoluwa Osuntokun
parent a77b1e00d5
commit 6e5f650be9
15 changed files with 114 additions and 113 deletions

View file

@ -19,24 +19,24 @@ import (
)
const (
// cbfIndexName is the human-readable name for the index.
cbfIndexName = "committed bloom filter index"
// cfIndexName is the human-readable name for the index.
cfIndexName = "committed bloom filter index"
)
var (
// cbfIndexKey is the name of the db bucket used to house the
// block hash -> CBF index.
cbfIndexKey = []byte("cbfbyhashidx")
// cfIndexKey is the name of the db bucket used to house the
// block hash -> CF index.
cfIndexKey = []byte("cfbyhashidx")
// errNoCBFEntry is an error that indicates a requested entry does
// not exist in the CBF index.
errCBFEntry = errors.New("no entry in the block ID index")
// errNoCFEntry is an error that indicates a requested entry does
// not exist in the CF index.
errCFEntry = errors.New("no entry in the block ID index")
)
func dbFetchCBFIndexEntry(dbTx database.Tx, blockHash *chainhash.Hash) ([]byte,
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(cbfIndexKey)
index := dbTx.Metadata().Bucket(cfIndexKey)
serializedFilter := index.Get(blockHash[:])
if len(serializedFilter) == 0 {
return nil, nil
@ -45,52 +45,52 @@ func dbFetchCBFIndexEntry(dbTx database.Tx, blockHash *chainhash.Hash) ([]byte,
return serializedFilter, nil
}
// The serialized format for keys and values in the block hash to CBF bucket is:
// <hash> = <CBF>
// The serialized format for keys and values in the block hash to CF bucket is:
// <hash> = <CF>
//
// Field Type Size
// hash chainhash.Hash 32 bytes
// CBF []byte variable
// CF []byte variable
// -----
// Total: > 32 bytes
// CBFIndex implements a CBF by hash index.
type CBFIndex struct {
// CFIndex implements a CF by hash index.
type CFIndex struct {
db database.DB
}
// Ensure the CBFIndex type implements the Indexer interface.
var _ Indexer = (*CBFIndex)(nil)
// Ensure the CFIndex type implements the Indexer interface.
var _ Indexer = (*CFIndex)(nil)
// Init initializes the hash-based CBF index.
// Init initializes the hash-based CF index.
//
// This is part of the Indexer interface.
func (idx *CBFIndex) Init() error {
func (idx *CFIndex) Init() error {
return nil
}
// Key returns the database key to use for the index as a byte slice.
//
// This is part of the Indexer interface.
func (idx *CBFIndex) Key() []byte {
return cbfIndexKey
func (idx *CFIndex) Key() []byte {
return cfIndexKey
}
// Name returns the human-readable name of the index.
//
// This is part of the Indexer interface.
func (idx *CBFIndex) Name() string {
return cbfIndexName
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 the buckets for the hash-based
// CBF index.
// CF index.
//
// This is part of the Indexer interface.
func (idx *CBFIndex) Create(dbTx database.Tx) error {
func (idx *CFIndex) Create(dbTx database.Tx) error {
meta := dbTx.Metadata()
_, err := meta.CreateBucket(cbfIndexKey)
_, err := meta.CreateBucket(cfIndexKey)
return err
}
@ -118,17 +118,17 @@ func generateFilterForBlock(block *btcutil.Block) ([]byte, error) {
return nil, err
}
fmt.Fprintf(os.Stderr, "Generated CBF for block %v", block.Hash())
fmt.Fprintf(os.Stderr, "Generated CF for block %v", block.Hash())
return filter.Bytes(), nil
}
// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain. This indexer adds a hash-to-CBF mapping for
// 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 *CBFIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
func (idx *CFIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
view *blockchain.UtxoViewpoint) error {
filterBytes, err := generateFilterForBlock(block)
if err != nil {
@ -136,55 +136,55 @@ func (idx *CBFIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
}
meta := dbTx.Metadata()
index := meta.Bucket(cbfIndexKey)
index := meta.Bucket(cfIndexKey)
err = index.Put(block.Hash()[:], filterBytes)
if err != nil {
return err
}
fmt.Fprintf(os.Stderr, "Stored CBF for block %v", block.Hash())
fmt.Fprintf(os.Stderr, "Stored CF for block %v", block.Hash())
return nil
}
// DisconnectBlock is invoked by the index manager when a block has been
// disconnected from the main chain. This indexer removes the hash-to-CBF
// 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 *CBFIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
func (idx *CFIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
view *blockchain.UtxoViewpoint) error {
index := dbTx.Metadata().Bucket(cbfIndexKey)
index := dbTx.Metadata().Bucket(cfIndexKey)
filterBytes := index.Get(block.Hash()[:])
if len(filterBytes) == 0 {
return fmt.Errorf("can't remove non-existent filter %s from " +
"the cbfilter index", block.Hash())
"the cfilter index", block.Hash())
}
return index.Delete(block.Hash()[:])
}
func (idx *CBFIndex) 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 = dbFetchCBFIndexEntry(dbTx, hash)
filterBytes, err = dbFetchCFIndexEntry(dbTx, hash)
return err
})
return filterBytes, err
}
// NewCBFIndex 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.
//
// 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 NewCBFIndex(db database.DB) *CBFIndex {
return &CBFIndex{db: db}
func NewCFIndex(db database.DB) *CFIndex {
return &CFIndex{db: db}
}
// DropCBFIndex drops the CBF index from the provided database if exists.
func DropCBFIndex(db database.DB) error {
return dropIndex(db, cbfIndexKey, cbfIndexName)
// DropCFIndex drops the CF index from the provided database if exists.
func DropCFIndex(db database.DB) error {
return dropIndex(db, cfIndexKey, cfIndexName)
}

View file

@ -278,15 +278,15 @@ func NewGetBlockTemplateCmd(request *TemplateRequest) *GetBlockTemplateCmd {
Request: request,
}
}
// GetCBFilterCmd defines the getcbfilter JSON-RPC command.
type GetCBFilterCmd struct {
// GetCFilterCmd defines the getcfilter JSON-RPC command.
type GetCFilterCmd struct {
Hash string
}
// NewGetCBFilterCmd returns a new instance which can be used to issue a
// getcbfilter JSON-RPC command.
func NewGetCBFilterCmd(hash string) *GetCBFilterCmd {
return &GetCBFilterCmd{
// NewGetCFilterCmd returns a new instance which can be used to issue a
// getcfilter JSON-RPC command.
func NewGetCFilterCmd(hash string) *GetCFilterCmd {
return &GetCFilterCmd{
Hash: hash,
}
}
@ -768,7 +768,7 @@ func init() {
MustRegisterCmd("getblockhash", (*GetBlockHashCmd)(nil), flags)
MustRegisterCmd("getblockheader", (*GetBlockHeaderCmd)(nil), flags)
MustRegisterCmd("getblocktemplate", (*GetBlockTemplateCmd)(nil), flags)
MustRegisterCmd("getcbfilter", (*GetCBFilterCmd)(nil), flags)
MustRegisterCmd("getcfilter", (*GetCFilterCmd)(nil), flags)
MustRegisterCmd("getchaintips", (*GetChainTipsCmd)(nil), flags)
MustRegisterCmd("getconnectioncount", (*GetConnectionCountCmd)(nil), flags)
MustRegisterCmd("getdifficulty", (*GetDifficultyCmd)(nil), flags)

View file

@ -318,15 +318,15 @@ func TestChainSvrCmds(t *testing.T) {
},
},
{
name: "getcbfilter",
name: "getcfilter",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("getcbfilter", "123")
return btcjson.NewCmd("getcfilter", "123")
},
staticCmd: func() interface{} {
return btcjson.NewGetCBFilterCmd("123")
return btcjson.NewGetCFilterCmd("123")
},
marshalled: `{"jsonrpc":"1.0","method":"getcbfilter","params":["123"],"id":1}`,
unmarshalled: &btcjson.GetCBFilterCmd{
marshalled: `{"jsonrpc":"1.0","method":"getcfilter","params":["123"],"id":1}`,
unmarshalled: &btcjson.GetCFilterCmd{
Hash: "123",
},
},

View file

@ -150,7 +150,7 @@ type config struct {
BlockPrioritySize uint32 `long:"blockprioritysize" description:"Size in bytes for high-priority/low-fee transactions when creating a block"`
UserAgentComments []string `long:"uacomment" description:"Comment to add to the user agent -- See BIP 14 for more information."`
NoPeerBloomFilters bool `long:"nopeerbloomfilters" description:"Disable bloom filtering support"`
NoCBFilters bool `long:"nocbfilters" description:"Disable committed bloom filtering (CBF) support"`
NoCFilters bool `long:"nocfilters" description:"Disable committed filtering (CF) support"`
SigCacheMaxSize uint `long:"sigcachemaxsize" description:"The maximum number of entries in the signature verification cache"`
BlocksOnly bool `long:"blocksonly" description:"Do not accept transactions from remote peers."`
TxIndex bool `long:"txindex" description:"Maintain a full hash-based transaction index which makes all transactions available via the getrawtransaction RPC"`

2
doc.go
View file

@ -112,7 +112,7 @@ Application Options:
--blockprioritysize= Size in bytes for high-priority/low-fee transactions
when creating a block (50000)
--nopeerbloomfilters Disable bloom filtering support.
--nocbfilters Disable committed bloom filtering (CBF) support.
--nocfilters Disable committed filtering (CF) support.
--sigcachemaxsize= The maximum number of entries in the signature
verification cache.
--blocksonly Do not accept transactions from remote peers.

View file

@ -144,9 +144,9 @@ type MessageListeners struct {
// message.
OnGetHeaders func(p *Peer, msg *wire.MsgGetHeaders)
// OnGetCBFilter is invoked when a peer receives a getcbfilter bitcoin
// OnGetCFilter is invoked when a peer receives a getcfilter bitcoin
// message.
OnGetCBFilter func(p *Peer, msg *wire.MsgGetCBFilter)
OnGetCFilter func(p *Peer, msg *wire.MsgGetCFilter)
// OnFeeFilter is invoked when a peer receives a feefilter bitcoin message.
OnFeeFilter func(p *Peer, msg *wire.MsgFeeFilter)
@ -1285,7 +1285,7 @@ func (p *Peer) maybeAddDeadline(pendingResponses map[string]time.Time, msgCmd st
deadline = time.Now().Add(stallResponseTimeout * 3)
pendingResponses[wire.CmdHeaders] = deadline
// XXX pedro: we may need to handle OnCBFilter here depending on the
// XXX pedro: we may need to handle OnCFilter here depending on the
// protocol behaviour defined.
}
}
@ -1586,9 +1586,9 @@ out:
p.cfg.Listeners.OnGetHeaders(p, msg)
}
case *wire.MsgGetCBFilter:
if p.cfg.Listeners.OnGetCBFilter != nil {
p.cfg.Listeners.OnGetCBFilter(p, msg)
case *wire.MsgGetCFilter:
if p.cfg.Listeners.OnGetCFilter != nil {
p.cfg.Listeners.OnGetCFilter(p, msg)
}
case *wire.MsgFeeFilter:

View file

@ -399,7 +399,7 @@ func TestPeerListeners(t *testing.T) {
OnGetHeaders: func(p *peer.Peer, msg *wire.MsgGetHeaders) {
ok <- msg
},
OnGetCBFilter: func(p *peer.Peer, msg *wire.MsgGetCBFilter) {
OnGetCFilter: func(p *peer.Peer, msg *wire.MsgGetCFilter) {
ok <- msg
},
OnFeeFilter: func(p *peer.Peer, msg *wire.MsgFeeFilter) {
@ -526,8 +526,8 @@ func TestPeerListeners(t *testing.T) {
wire.NewMsgGetHeaders(),
},
{
"OnGetCBFilter",
wire.NewMsgGetCBFilter(&chainhash.Hash{}),
"OnGetCFilter",
wire.NewMsgGetCFilter(&chainhash.Hash{}),
},
{
"OnFeeFilter",

View file

@ -142,7 +142,7 @@ var rpcHandlersBeforeInit = map[string]commandHandler{
"getblockhash": handleGetBlockHash,
"getblockheader": handleGetBlockHeader,
"getblocktemplate": handleGetBlockTemplate,
"getcbfilter": handleGetCBFilter,
"getcfilter": handleGetCFilter,
"getconnectioncount": handleGetConnectionCount,
"getcurrentnet": handleGetCurrentNet,
"getdifficulty": handleGetDifficulty,
@ -259,7 +259,7 @@ var rpcLimited = map[string]struct{}{
"getblockcount": {},
"getblockhash": {},
"getblockheader": {},
"getcbfilter": {},
"getcfilter": {},
"getcurrentnet": {},
"getdifficulty": {},
"getheaders": {},
@ -2146,19 +2146,20 @@ func handleGetBlockTemplate(s *rpcServer, cmd interface{}, closeChan <-chan stru
}
}
// handleGetCBFilter implements the getcbfilter command.
func handleGetCBFilter(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.GetCBFilterCmd)
// handleGetCFilter implements the getcfilter command.
func handleGetCFilter(s *rpcServer, cmd interface{}, closeChan <-chan struct{}) (interface{}, error) {
c := cmd.(*btcjson.GetCFilterCmd)
hash, err := chainhash.NewHashFromStr(c.Hash)
if err != nil {
return nil, rpcDecodeHexError(c.Hash)
}
filterBytes, err := s.server.cbfIndex.FilterByBlockHash(hash)
filterBytes, err := s.server.cfIndex.FilterByBlockHash(hash)
if len(filterBytes) > 0 {
rpcsLog.Debugf("Found CB filter for %v", hash)
rpcsLog.Debugf("Found committed filter for %v", hash)
} else {
rpcsLog.Debugf("Could not find CB filter for %v: %v", hash, err)
rpcsLog.Debugf("Could not find committed filter for %v: %v",
hash, err)
return nil, &btcjson.RPCError{
Code: btcjson.ErrRPCBlockNotFound,
Message: "Block not found",

View file

@ -323,10 +323,10 @@ var helpDescsEnUS = map[string]string{
"getblocktemplate--condition2": "mode=proposal, accepted",
"getblocktemplate--result1": "An error string which represents why the proposal was rejected or nothing if accepted",
// GetCBFilterCmd help.
"getcbfilter--synopsis": "Returns a block's committed bloom filter given its hash.",
"getcbfilter-hash": "The hash of the block",
"getcbfilter--result0": "The block's committed bloom filter",
// GetCFilterCmd help.
"getcfilter--synopsis": "Returns a block's committed filter given its hash.",
"getcfilter-hash": "The hash of the block",
"getcfilter--result0": "The block's committed filter",
// GetConnectionCountCmd help.
"getconnectioncount--synopsis": "Returns the number of active connections to other peers.",
@ -673,7 +673,7 @@ var rpcResultTypes = map[string][]interface{}{
"getblockheader": {(*string)(nil), (*btcjson.GetBlockHeaderVerboseResult)(nil)},
"getblocktemplate": {(*btcjson.GetBlockTemplateResult)(nil), (*string)(nil), nil},
"getblockchaininfo": {(*btcjson.GetBlockChainInfoResult)(nil)},
"getcbfilter": {(*string)(nil)},
"getcfilter": {(*string)(nil)},
"getconnectioncount": {(*int32)(nil)},
"getcurrentnet": {(*uint32)(nil)},
"getdifficulty": {(*float64)(nil)},

View file

@ -167,8 +167,8 @@
; Must not include characters '/', ':', '(' and ')'.
; uacomment=
; Disable committed peer bloom filtering (CBF).
; nocbfilters=1
; Disable committed peer filtering (CF).
; nocfilters=1
; ------------------------------------------------------------------------------
; RPC server options - The following options control the built-in RPC server

View file

@ -229,7 +229,7 @@ type server struct {
// do not need to be protected for concurrent access.
txIndex *indexers.TxIndex
addrIndex *indexers.AddrIndex
cbfIndex *indexers.CBFIndex
cfIndex *indexers.CFIndex
}
// serverPeer extends the peer to maintain state shared by the server and
@ -739,14 +739,14 @@ func (sp *serverPeer) OnGetHeaders(_ *peer.Peer, msg *wire.MsgGetHeaders) {
sp.QueueMessage(&wire.MsgHeaders{Headers: blockHeaders}, nil)
}
// OnGetCBFilter is invoked when a peer receives a getcbfilter bitcoin message.
func (sp *serverPeer) OnGetCBFilter(_ *peer.Peer, msg *wire.MsgGetCBFilter) {
// Ignore getcbfilter requests if not in sync.
// OnGetCFilter is invoked when a peer receives a getcfilter bitcoin message.
func (sp *serverPeer) OnGetCFilter(_ *peer.Peer, msg *wire.MsgGetCFilter) {
// Ignore getcfilter requests if not in sync.
if !sp.server.blockManager.IsCurrent() {
return
}
filterBytes, err := sp.server.cbfIndex.FilterByBlockHash(&msg.BlockHash)
filterBytes, err := sp.server.cfIndex.FilterByBlockHash(&msg.BlockHash)
if len(filterBytes) > 0 {
peerLog.Infof("Obtained CB filter for %v", msg.BlockHash)
@ -1603,7 +1603,7 @@ func newPeerConfig(sp *serverPeer) *peer.Config {
OnGetData: sp.OnGetData,
OnGetBlocks: sp.OnGetBlocks,
OnGetHeaders: sp.OnGetHeaders,
OnGetCBFilter: sp.OnGetCBFilter,
OnGetCFilter: sp.OnGetCFilter,
OnFeeFilter: sp.OnFeeFilter,
OnFilterAdd: sp.OnFilterAdd,
OnFilterClear: sp.OnFilterClear,
@ -2178,8 +2178,8 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
if cfg.NoPeerBloomFilters {
services &^= wire.SFNodeBloom
}
if cfg.NoCBFilters {
services &^= wire.SFNodeCBF
if cfg.NoCFilters {
services &^= wire.SFNodeCF
}
amgr := addrmgr.New(cfg.DataDir, btcdLookup)
@ -2243,10 +2243,10 @@ func newServer(listenAddrs []string, db database.DB, chainParams *chaincfg.Param
s.addrIndex = indexers.NewAddrIndex(db, chainParams)
indexes = append(indexes, s.addrIndex)
}
if !cfg.NoCBFilters {
indxLog.Info("CBF index is enabled")
s.cbfIndex = indexers.NewCBFIndex(db)
indexes = append(indexes, s.cbfIndex)
if !cfg.NoCFilters {
indxLog.Info("CF index is enabled")
s.cfIndex = indexers.NewCFIndex(db)
indexes = append(indexes, s.cfIndex)
}
// Create an index manager if any of the optional indexes are enabled.

View file

@ -51,7 +51,7 @@ const (
CmdReject = "reject"
CmdSendHeaders = "sendheaders"
CmdFeeFilter = "feefilter"
CmdGetCBFilter = "getcbfilter"
CmdGetCFilter = "getcfilter"
)
// MessageEncoding represents the wire message encoding format to be used.
@ -157,8 +157,8 @@ func makeEmptyMessage(command string) (Message, error) {
case CmdFeeFilter:
msg = &MsgFeeFilter{}
case CmdGetCBFilter:
msg = &MsgGetCBFilter{}
case CmdGetCFilter:
msg = &MsgGetCFilter{}
default:
return nil, fmt.Errorf("unhandled command [%s]", command)

View file

@ -10,39 +10,39 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
)
type MsgGetCBFilter struct {
type MsgGetCFilter struct {
ProtocolVersion uint32
BlockHash chainhash.Hash
}
func (msg *MsgGetCBFilter) BtcDecode(r io.Reader, pver uint32) error {
func (msg *MsgGetCFilter) BtcDecode(r io.Reader, pver uint32) error {
return readElement(r, &msg.BlockHash)
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
// This is part of the Message interface implementation.
func (msg *MsgGetCBFilter) BtcEncode(w io.Writer, pver uint32) error {
func (msg *MsgGetCFilter) BtcEncode(w io.Writer, pver uint32) error {
return writeElement(w, &msg.BlockHash)
}
// Command returns the protocol command string for the message. This is part
// of the Message interface implementation.
func (msg *MsgGetCBFilter) Command() string {
return CmdGetCBFilter
func (msg *MsgGetCFilter) Command() string {
return CmdGetCFilter
}
// MaxPayloadLength returns the maximum length the payload can be for the
// receiver. This is part of the Message interface implementation.
func (msg *MsgGetCBFilter) MaxPayloadLength(pver uint32) uint32 {
func (msg *MsgGetCFilter) MaxPayloadLength(pver uint32) uint32 {
// Protocol version 4 bytes + block hash.
return 4 + chainhash.HashSize
}
// NewMsgGetCBFilter returns a new bitcoin getblocks message that conforms to
// NewMsgGetCFilter returns a new bitcoin getblocks message that conforms to
// the Message interface using the passed parameters and defaults for the
// remaining fields.
func NewMsgGetCBFilter(blockHash *chainhash.Hash) *MsgGetCBFilter {
return &MsgGetCBFilter{
func NewMsgGetCFilter(blockHash *chainhash.Hash) *MsgGetCFilter {
return &MsgGetCFilter{
ProtocolVersion: ProtocolVersion,
BlockHash: *blockHash,
}

View file

@ -72,9 +72,9 @@ const (
// and transactions including witness data (BIP0144).
SFNodeWitness
// SFNNodeCBF is a flag used to indicate a peer supports committed
// bloom filters (CBFs).
SFNodeCBF
// SFNodeCF is a flag used to indicate a peer supports committed
// filters (CFs).
SFNodeCF
)
// Map of service flags back to their constant names for pretty printing.
@ -83,7 +83,7 @@ var sfStrings = map[ServiceFlag]string{
SFNodeGetUTXO: "SFNodeGetUTXO",
SFNodeBloom: "SFNodeBloom",
SFNodeWitness: "SFNodeWitness",
SFNodeCBF: "SFNodeCBF",
SFNodeCF: "SFNodeCF",
}
// orderedSFStrings is an ordered list of service flags from highest to
@ -93,7 +93,7 @@ var orderedSFStrings = []ServiceFlag{
SFNodeGetUTXO,
SFNodeBloom,
SFNodeWitness,
SFNodeCBF,
SFNodeCF,
}
// String returns the ServiceFlag in human-readable form.

View file

@ -18,8 +18,8 @@ func TestServiceFlagStringer(t *testing.T) {
{SFNodeBloom, "SFNodeBloom"},
{SFNodeWitness, "SFNodeWitness"},
{0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|SFNodeBloom|SFNodeWitness|0xfffffff0"},
{SFNodeCBF, "SFNodeCBF"},
{0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|SFNodeBloom|SFNodeCBF|0xfffffff0"},
{SFNodeCF, "SFNodeCF"},
{0xffffffff, "SFNodeNetwork|SFNodeGetUTXO|SFNodeBloom|SFNodeCF|0xfffffff0"},
}
t.Logf("Running %d tests", len(tests))