Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add new getcfilters message for utreexo nodes #197

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6418f02
add function to create headers for utreexo cf
alainjr10 Jul 23, 2024
cab02cc
add new filter type
alainjr10 Jul 23, 2024
1f938ff
add indexer for utreexoc filter
alainjr10 Jul 23, 2024
a3e6852
add func to serialize utreexo roots hash
alainjr10 Jul 23, 2024
001749f
handle new getcfilters and getcfheaders message type
alainjr10 Jul 23, 2024
f509bc8
add utreexocf command line params to config struct
alainjr10 Jul 23, 2024
afae8ca
add checks and error messages on trying to disable utreexocf index
alainjr10 Jul 23, 2024
5254208
update implementeation of getcfilter handler
alainjr10 Jul 23, 2024
f708806
fix: fix node panic on start
alainjr10 Jul 25, 2024
e7b1d55
add function to filter headers by blockhashes
alainjr10 Aug 4, 2024
04b6da9
handle on getcfcheckpt in server.go
alainjr10 Aug 4, 2024
5573d34
add filterheaderby block hash and improve other util funcs
alainjr10 Aug 4, 2024
5383c21
fetch hilter headers from proper location ongetcfheaders based on fil…
alainjr10 Aug 4, 2024
5939086
update handlegetcfilterheader to handle both supported cfilters
alainjr10 Aug 4, 2024
6394bbc
add comment for new utreexocfilter type
alainjr10 Aug 5, 2024
e74b647
adjust utreexoCFIndexName comment
alainjr10 Aug 5, 2024
16de187
indexers: make more appropriate comments
alainjr10 Aug 5, 2024
068bbea
indexer: init chain in utreexocfindex init
alainjr10 Aug 6, 2024
c6452a4
utreexod: update order of append indexers
alainjr10 Aug 6, 2024
0e9a41a
indexer: return empty utreexo stump if root is nil
alainjr10 Aug 8, 2024
e84894d
add missing return for absent utreexocfindex for pruned nodes
alainjr10 Aug 12, 2024
4585387
rpc: handle getutreexocfilter in different function
alainjr10 Aug 12, 2024
271fa62
wire: add sfnodeutreexocf flag
alainjr10 Aug 20, 2024
d0a1962
indexers: handle blocking dureing check for CSN
alainjr10 Aug 20, 2024
146a032
server: add check for sfnodeutreexocf flag
alainjr10 Aug 20, 2024
51df5ba
indexers: fix utreexocfilters not performing ibd
alainjr10 Aug 30, 2024
5ba9fbf
config: throw early error when noutreexo is passed along with utreexo…
alainjr10 Aug 30, 2024
5cb8c30
indexers: update fetchutreexoroots func to fetch proper roots for all…
alainjr10 Aug 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 301 additions & 0 deletions blockchain/indexers/utreexocfindex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
package indexers

import (
"errors"

"github.com/utreexo/utreexod/blockchain"
"github.com/utreexo/utreexod/btcutil"
"github.com/utreexo/utreexod/btcutil/gcs/builder"
"github.com/utreexo/utreexod/chaincfg"
"github.com/utreexo/utreexod/chaincfg/chainhash"
"github.com/utreexo/utreexod/database"
"github.com/utreexo/utreexod/wire"
)

// utreexoProofIndexName is the human-readable name for the index.
alainjr10 marked this conversation as resolved.
Show resolved Hide resolved
const (
utreexoCFIndexName = "utreexo custom cfilter index"
)

// utreexocfilter is a custom commited filter which serves utreexo roots
// these roots are already present, so they need not be created/stored, their
// headers could be stored though
var (
// utreexoCFIndexParentBucketKey is the name of the parent bucket used to
// house the index. The rest of the buckets live below this bucket.
utreexoCFIndexParentBucketKey = []byte("utreexocfindexparentbucket")

// utreexoCfHeaderKeys is an array of db bucket names used to house indexes of
// block hashes to cf headers.
utreexoCfHeaderKeys = [][]byte{
[]byte("utreexocfheaderbyhashidx"),
}
)

// dbFetchFilterIdxEntry retrieves a data blob from the filter index database.
// An entry's absence is not considered an error.
func dbFetchUtreexoCFilterIdxEntry(dbTx database.Tx, key []byte, h *chainhash.Hash) ([]byte, error) {
idx := dbTx.Metadata().Bucket(utreexoCFIndexParentBucketKey).Bucket(key)
return idx.Get(h[:]), nil
}

// dbStoreFilterIdxEntry stores a data blob in the filter index database.
func dbStoreUtreexoCFilterIdxEntry(dbTx database.Tx, key []byte, h *chainhash.Hash, f []byte) error {
idx := dbTx.Metadata().Bucket(utreexoCFIndexParentBucketKey).Bucket(key)
return idx.Put(h[:], f)
}

// dbDeleteFilterIdxEntry deletes a data blob from the filter index database.
func dbDeleteUtreexoCFilterIdxEntry(dbTx database.Tx, key []byte, h *chainhash.Hash) error {
idx := dbTx.Metadata().Bucket(utreexoCFIndexParentBucketKey).Bucket(key)
return idx.Delete(h[:])
}

var _ Indexer = (*UtreexoCFIndex)(nil)

var _ NeedsInputser = (*UtreexoCFIndex)(nil)

type UtreexoCFIndex struct {
db database.DB
chainParams *chaincfg.Params

chain *blockchain.BlockChain

utreexoProofIndex *UtreexoProofIndex

flatUtreexoProofIndex *FlatUtreexoProofIndex
}

func (idx *UtreexoCFIndex) NeedsInputs() bool {
return true
}

// Init initializes the utreexo cf index. This is part of the Indexer
// interface.
func (idx *UtreexoCFIndex) Init(_ *blockchain.BlockChain) 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 *UtreexoCFIndex) Key() []byte {
return utreexoCFIndexParentBucketKey
}

// Name returns the human-readable name of the index. This is part of the
// Indexer interface.
func (idx *UtreexoCFIndex) Name() string {
return utreexoCFIndexName
}

// Create is invoked when the index manager determines the index needs to
// be created for the first time. It creates buckets for the custom utreexo
// filter index.
func (idx *UtreexoCFIndex) Create(dbTx database.Tx) error {
meta := dbTx.Metadata()

utreexoCfIndexParentBucket, err := meta.CreateBucket(utreexoCFIndexParentBucketKey)
if err != nil {
return err
}

for _, bucketName := range utreexoCfHeaderKeys {
_, err = utreexoCfIndexParentBucket.CreateBucket(bucketName)
if err != nil {
return err
}
}

return nil
}

// storeUtreexoCFilter stores a given utreexocfilter header
func storeUtreexoCFHeader(dbTx database.Tx, block *btcutil.Block, filterData []byte,
filterType wire.FilterType) error {
if filterType != wire.UtreexoCFilter {
return errors.New("invalid filter type")
}

// Figure out which header bucket to use.
hkey := utreexoCfHeaderKeys[0]
h := block.Hash()

// fetch the previous block's filter header.
var prevHeader *chainhash.Hash
ph := &block.MsgBlock().Header.PrevBlock
if ph.IsEqual(&zeroHash) {
prevHeader = &zeroHash
} else {
pfh, err := dbFetchUtreexoCFilterIdxEntry(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, err := builder.MakeHeaderForUtreexoCFilter(filterData, *prevHeader)
if err != nil {
return err
}
return dbStoreUtreexoCFilterIdxEntry(dbTx, hkey, h, fh[:])
}

// ConnectBlock is invoked by the index manager when a new block has been
// connected to the main chain.
// This is part of the Indexer interface.
func (idx *UtreexoCFIndex) ConnectBlock(dbTx database.Tx, block *btcutil.Block,
stxos []blockchain.SpentTxOut) error {

blockHash := block.Hash()
roots, leaves, err := idx.fetchUtreexoRoots(dbTx, blockHash)
alainjr10 marked this conversation as resolved.
Show resolved Hide resolved

if err != nil {
return err
}

// serialize the hashes of the utreexo roots hash
serializedUtreexo, err := blockchain.SerializeUtreexoRootsHash(leaves, roots)
if err != nil {
return err
}

return storeUtreexoCFHeader(dbTx, block, serializedUtreexo, wire.UtreexoCFilter)
}

// fetches the utreexo roots for a given block hash
func (idx *UtreexoCFIndex) fetchUtreexoRoots(dbTx database.Tx,
blockHash *chainhash.Hash) ([]*chainhash.Hash, uint64, error) {

var leaves uint64
var roots []*chainhash.Hash

// For compact state nodes
if idx.chain.IsUtreexoViewActive() && idx.chain.IsAssumeUtreexo() {
viewPoint, err := idx.chain.FetchUtreexoViewpoint(blockHash)
if err != nil {
return nil, 0, err
}
roots = viewPoint.GetRoots()
leaves = viewPoint.NumLeaves()
}
// for bridge nodes
if idx.utreexoProofIndex != nil {
roots, leaves, err := idx.utreexoProofIndex.FetchUtreexoState(dbTx, blockHash)
if err != nil {
return nil, 0, err
}
return roots, leaves, nil
} else if idx.flatUtreexoProofIndex != nil {
height, err := idx.chain.BlockHeightByHash(blockHash)
alainjr10 marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, 0, err
}
roots, leaves, err := idx.flatUtreexoProofIndex.FetchUtreexoState(height)
if err != nil {
return nil, 0, err
}
return roots, leaves, nil
}

return roots, leaves, nil
}

// 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 *UtreexoCFIndex) DisconnectBlock(dbTx database.Tx, block *btcutil.Block,
_ []blockchain.SpentTxOut) error {

for _, key := range utreexoCfHeaderKeys {
err := dbDeleteUtreexoCFilterIdxEntry(dbTx, key, block.Hash())
if err != nil {
return err
}
}

return nil
}

// PruneBlock is invoked when an older block is deleted after it's been
// processed.
// TODO (kcalvinalvin): Consider keeping the filters at a later date to help with
// reindexing as a pruned node.
//
// This is part of the Indexer interface.
func (idx *UtreexoCFIndex) PruneBlock(dbTx database.Tx, blockHash *chainhash.Hash) error {

for _, key := range utreexoCfHeaderKeys {
err := dbDeleteUtreexoCFilterIdxEntry(dbTx, key, blockHash)
if err != nil {
return err
}
}

return nil
}

// entryByBlockHash fetches a filter index entry of a particular type
// (eg. filter, filter header, etc) for a filter type and block hash.
func (idx *UtreexoCFIndex) entryByBlockHash(dbTx database.Tx,
filterType wire.FilterType, h *chainhash.Hash) ([]byte, error) {

if uint8(filterType) != uint8(wire.UtreexoCFilter) {
return nil, errors.New("unsupported filter type")
}

roots, leaves, err := idx.fetchUtreexoRoots(dbTx, h)

if err != nil {
return nil, err
}

// serialize the hashes of the utreexo roots hash
serializedUtreexo, err := blockchain.SerializeUtreexoRootsHash(leaves, roots)
if err != nil {
return nil, err
}

return serializedUtreexo, err
}

// FilterByBlockHash returns the serialized contents of a block's utreexo
// cfilter.
func (idx *UtreexoCFIndex) FilterByBlockHash(dbTx database.Tx, h *chainhash.Hash,
filterType wire.FilterType) ([]byte, error) {
return idx.entryByBlockHash(dbTx, filterType, h)
}

// 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 NewUtreexoCfIndex(db database.DB, chainParams *chaincfg.Params, utreexoProofIndex *UtreexoProofIndex,
flatUtreexoProofIndex *FlatUtreexoProofIndex) *UtreexoCFIndex {
return &UtreexoCFIndex{db: db, chainParams: chainParams, utreexoProofIndex: utreexoProofIndex,
flatUtreexoProofIndex: flatUtreexoProofIndex}
}

// DropCfIndex drops the CF index from the provided database if exists.
func DropUtreexoCfIndex(db database.DB, interrupt <-chan struct{}) error {
return dropIndex(db, utreexoCFIndexParentBucketKey, utreexoCFIndexName, interrupt)
}

// CfIndexInitialized returns true if the cfindex has been created previously.
func UtreexoCfIndexInitialized(db database.DB) bool {
var exists bool
db.View(func(dbTx database.Tx) error {
bucket := dbTx.Metadata().Bucket(utreexoCFIndexParentBucketKey)
exists = bucket != nil
return nil
})

return exists
}