Skip to content

Instantly share code, notes, and snippets.

@yyforyongyu
Last active May 25, 2021 08:18
Show Gist options
  • Save yyforyongyu/73a719eed611eeac5973ad8a933a538e to your computer and use it in GitHub Desktop.
Save yyforyongyu/73a719eed611eeac5973ad8a933a538e to your computer and use it in GitHub Desktop.
A simple golang script to parse the ffldb database files created by btcd.
package main
import (
"encoding/binary"
"flag"
"fmt"
_ "github.com/btcsuite/btcd/database/ffldb"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/database"
"github.com/btcsuite/btcd/wire"
"github.com/btcsuite/btcutil"
)
func main() {
dbPath := flag.String(
"dbpath",
"/home/ubuntu/.btcd/mainnet/blocks_ffldb",
"the btcd blocks ffldb file path",
)
startHeight := flag.Int(
"start", 0, "specify the blocks starting height",
)
endHeight := flag.Int(
"end", 0, "specify the blocks ending height. inclusive",
)
flag.Parse()
blocks, err := FetchBlocks(
*dbPath, int32(*startHeight), int32(*endHeight),
)
if err != nil {
fmt.Println(err)
return
}
for _, b := range blocks {
fmt.Printf("found %v\n", b.Height())
}
fmt.Printf("found %v blocks\n", len(blocks))
}
var (
// heightIndexBucketName is the name of the db bucket used to house to
// the block height -> block hash index.
heightIndexBucketName = []byte("heightidx")
)
// FetchBlocks opens the database and parses the blocks.
func FetchBlocks(dbPath string, start, end int32) ([]*btcutil.Block, error) {
// load btcd db file
db, err := database.Open("ffldb", dbPath, wire.MainNet)
if err != nil {
return nil, err
}
defer db.Close()
return dbFetchBlocks(db, start, end)
}
func dbFetchBlocks(db database.DB, start, end int32) ([]*btcutil.Block, error) {
var blocks []*btcutil.Block
var err error
err = db.View(func(tx database.Tx) error {
blocks, err = bulkFetchBlocks(tx, start, end)
return err
})
if err != nil {
return nil, err
}
return blocks, nil
}
// bulkFetchBlocks uses an existing database bucket to retrieve a
// collection of block hashes for the provided height range.
// Note that the start and end values are inclusive. For instance, start=0,
// end=10 will fetch for block 0 till block 10, a total of 11 blocks.
func bulkFetchBlocks(tx database.Tx,
start, end int32) ([]*btcutil.Block, error) {
meta := tx.Metadata()
heightIndexBucket := meta.Bucket(heightIndexBucketName)
// make a slice to avoid copy
blocks := make([]*btcutil.Block, end-start+1)
// fetchHash is a closure to fetch block hash by a given height.
fetchHash := func(height int32) (*chainhash.Hash, error) {
var serializedHeight [4]byte
binary.LittleEndian.PutUint32(
serializedHeight[:], uint32(height),
)
hashBytes := heightIndexBucket.Get(serializedHeight[:])
if hashBytes == nil {
return nil, fmt.Errorf(
"no block at height %d exists", height,
)
}
var hash chainhash.Hash
copy(hash[:], hashBytes)
return &hash, nil
}
fetchBlock := func(hash *chainhash.Hash) (*btcutil.Block, error) {
// Use the View function of the database to perform a managed
// read-only transaction and fetch the block stored.
blockBytes, err := tx.FetchBlock(hash)
if err != nil {
return nil, err
}
block, err := btcutil.NewBlockFromBytes(blockBytes)
if err != nil {
return nil, err
}
return block, nil
}
for i := range blocks {
// Calculate the height and encode
height := start + int32(i)
hash, err := fetchHash(height)
if err != nil {
return nil, err
}
block, err := fetchBlock(hash)
if err != nil {
return nil, err
}
// assign the height
block.SetHeight(height)
blocks[i] = block
}
return blocks, nil
}
@asemoon
Copy link

asemoon commented May 25, 2021

Get the following error when running this on the testnet:
block data for block 683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6 is for the wrong network - got 303307798, want 3669344250
how can this script be changed to support test net?

@yyforyongyu
Copy link
Author

Get the following error when running this on the testnet:
block data for block 683e86bd5c6d110d91b94b97137ba6bfe02dbbdb8e3dff722a669b5d69d77af6 is for the wrong network - got 303307798, want 3669344250
how can this script be changed to support test net?

I think you only need to point the dbpath to your testnet files, not mainnet/blocks_ffldb but testnet/blocks_ffldb.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment