blockchain: Store block headers in bucket managed by chainio.
The bucket contains block headers keyed by the block height encoded as big-endian concatenated with the block hash. This allows block headers to be fetched from the DB in height order with a cursor.
This commit is contained in:
parent
358aed20b7
commit
175fd940bb
3 changed files with 77 additions and 18 deletions
|
@ -54,7 +54,7 @@ func (b *BlockChain) maybeAcceptBlock(block *btcutil.Block, flags BehaviorFlags)
|
|||
// such as making blocks that never become part of the main chain or
|
||||
// blocks that fail to connect available for further analysis.
|
||||
err = b.db.Update(func(dbTx database.Tx) error {
|
||||
return dbMaybeStoreBlock(dbTx, block)
|
||||
return dbStoreBlock(dbTx, block)
|
||||
})
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
|
@ -544,20 +544,6 @@ func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List
|
|||
return detachNodes, attachNodes
|
||||
}
|
||||
|
||||
// dbMaybeStoreBlock stores the provided block in the database if it's not
|
||||
// already there.
|
||||
func dbMaybeStoreBlock(dbTx database.Tx, block *btcutil.Block) error {
|
||||
hasBlock, err := dbTx.HasBlock(block.Hash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasBlock {
|
||||
return nil
|
||||
}
|
||||
|
||||
return dbTx.StoreBlock(block)
|
||||
}
|
||||
|
||||
// connectBlock handles connecting the passed node/block to the end of the main
|
||||
// (best) chain.
|
||||
//
|
||||
|
|
|
@ -18,7 +18,18 @@ import (
|
|||
"github.com/btcsuite/btcutil"
|
||||
)
|
||||
|
||||
const (
|
||||
// blockHdrSize is the size of a block header. This is simply the
|
||||
// constant from wire and is only provided here for convenience since
|
||||
// wire.MaxBlockHeaderPayload is quite long.
|
||||
blockHdrSize = wire.MaxBlockHeaderPayload
|
||||
)
|
||||
|
||||
var (
|
||||
// blockIndexBucketName is the name of the db bucket used to house to the
|
||||
// block headers and contextual information.
|
||||
blockIndexBucketName = []byte("blockheaderidx")
|
||||
|
||||
// hashIndexBucketName is the name of the db bucket used to house to the
|
||||
// block hash -> block height index.
|
||||
hashIndexBucketName = []byte("hashidx")
|
||||
|
@ -1058,6 +1069,7 @@ func dbPutBestState(dbTx database.Tx, snapshot *BestState, workSum *big.Int) err
|
|||
func (b *BlockChain) createChainState() error {
|
||||
// Create a new node from the genesis block and set it as the best node.
|
||||
genesisBlock := btcutil.NewBlock(b.chainParams.GenesisBlock)
|
||||
genesisBlock.SetHeight(0)
|
||||
header := &genesisBlock.MsgBlock().Header
|
||||
node := newBlockNode(header, 0)
|
||||
node.status = statusDataStored | statusValid
|
||||
|
@ -1077,10 +1089,17 @@ func (b *BlockChain) createChainState() error {
|
|||
// Create the initial the database chain state including creating the
|
||||
// necessary index buckets and inserting the genesis block.
|
||||
err := b.db.Update(func(dbTx database.Tx) error {
|
||||
meta := dbTx.Metadata()
|
||||
|
||||
// Create the bucket that houses the block index data.
|
||||
_, err := meta.CreateBucket(blockIndexBucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create the bucket that houses the chain block hash to height
|
||||
// index.
|
||||
meta := dbTx.Metadata()
|
||||
_, err := meta.CreateBucket(hashIndexBucketName)
|
||||
_, err = meta.CreateBucket(hashIndexBucketName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1120,7 +1139,7 @@ func (b *BlockChain) createChainState() error {
|
|||
}
|
||||
|
||||
// Store the genesis block into the database.
|
||||
return dbTx.StoreBlock(genesisBlock)
|
||||
return dbStoreBlock(dbTx, genesisBlock)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
@ -1273,6 +1292,60 @@ func dbFetchBlockByNode(dbTx database.Tx, node *blockNode) (*btcutil.Block, erro
|
|||
return block, nil
|
||||
}
|
||||
|
||||
// dbStoreBlock stores the provided block in the database. The block header is
|
||||
// written to the block index bucket and full block data is written to ffldb.
|
||||
func dbStoreBlockHeader(dbTx database.Tx, blockHeader *wire.BlockHeader, height uint32) error {
|
||||
// Serialize block data to be stored. This is just the serialized header.
|
||||
w := bytes.NewBuffer(make([]byte, 0, blockHdrSize))
|
||||
err := blockHeader.Serialize(w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
value := w.Bytes()
|
||||
|
||||
// Write block header data to block index bucket.
|
||||
blockHash := blockHeader.BlockHash()
|
||||
blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
|
||||
key := blockIndexKey(&blockHash, height)
|
||||
return blockIndexBucket.Put(key, value)
|
||||
}
|
||||
|
||||
// dbStoreBlock stores the provided block in the database. The block header is
|
||||
// written to the block index bucket and full block data is written to ffldb.
|
||||
func dbStoreBlock(dbTx database.Tx, block *btcutil.Block) error {
|
||||
if block.Height() == btcutil.BlockHeightUnknown {
|
||||
return fmt.Errorf("cannot store block %s with unknown height",
|
||||
block.Hash())
|
||||
}
|
||||
|
||||
// First store block header in the block index bucket.
|
||||
err := dbStoreBlockHeader(dbTx, &block.MsgBlock().Header,
|
||||
uint32(block.Height()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Then store block data in ffldb if we haven't already.
|
||||
hasBlock, err := dbTx.HasBlock(block.Hash())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hasBlock {
|
||||
return nil
|
||||
}
|
||||
return dbTx.StoreBlock(block)
|
||||
}
|
||||
|
||||
// blockIndexKey generates the binary key for an entry in the block index
|
||||
// bucket. The key is composed of the block height encoded as a big-endian
|
||||
// 32-bit unsigned int followed by the 32 byte block hash.
|
||||
func blockIndexKey(blockHash *chainhash.Hash, blockHeight uint32) []byte {
|
||||
indexKey := make([]byte, chainhash.HashSize+4)
|
||||
binary.BigEndian.PutUint32(indexKey[0:4], blockHeight)
|
||||
copy(indexKey[4:chainhash.HashSize+4], blockHash[:])
|
||||
return indexKey
|
||||
}
|
||||
|
||||
// BlockByHeight returns the block at the given height in the main chain.
|
||||
//
|
||||
// This function is safe for concurrent access.
|
||||
|
|
Loading…
Reference in a new issue