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
|
// such as making blocks that never become part of the main chain or
|
||||||
// blocks that fail to connect available for further analysis.
|
// blocks that fail to connect available for further analysis.
|
||||||
err = b.db.Update(func(dbTx database.Tx) error {
|
err = b.db.Update(func(dbTx database.Tx) error {
|
||||||
return dbMaybeStoreBlock(dbTx, block)
|
return dbStoreBlock(dbTx, block)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
|
|
@ -544,20 +544,6 @@ func (b *BlockChain) getReorganizeNodes(node *blockNode) (*list.List, *list.List
|
||||||
return detachNodes, attachNodes
|
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
|
// connectBlock handles connecting the passed node/block to the end of the main
|
||||||
// (best) chain.
|
// (best) chain.
|
||||||
//
|
//
|
||||||
|
|
|
@ -18,7 +18,18 @@ import (
|
||||||
"github.com/btcsuite/btcutil"
|
"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 (
|
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
|
// hashIndexBucketName is the name of the db bucket used to house to the
|
||||||
// block hash -> block height index.
|
// block hash -> block height index.
|
||||||
hashIndexBucketName = []byte("hashidx")
|
hashIndexBucketName = []byte("hashidx")
|
||||||
|
@ -1058,6 +1069,7 @@ func dbPutBestState(dbTx database.Tx, snapshot *BestState, workSum *big.Int) err
|
||||||
func (b *BlockChain) createChainState() error {
|
func (b *BlockChain) createChainState() error {
|
||||||
// Create a new node from the genesis block and set it as the best node.
|
// Create a new node from the genesis block and set it as the best node.
|
||||||
genesisBlock := btcutil.NewBlock(b.chainParams.GenesisBlock)
|
genesisBlock := btcutil.NewBlock(b.chainParams.GenesisBlock)
|
||||||
|
genesisBlock.SetHeight(0)
|
||||||
header := &genesisBlock.MsgBlock().Header
|
header := &genesisBlock.MsgBlock().Header
|
||||||
node := newBlockNode(header, 0)
|
node := newBlockNode(header, 0)
|
||||||
node.status = statusDataStored | statusValid
|
node.status = statusDataStored | statusValid
|
||||||
|
@ -1077,10 +1089,17 @@ func (b *BlockChain) createChainState() error {
|
||||||
// Create the initial the database chain state including creating the
|
// Create the initial the database chain state including creating the
|
||||||
// necessary index buckets and inserting the genesis block.
|
// necessary index buckets and inserting the genesis block.
|
||||||
err := b.db.Update(func(dbTx database.Tx) error {
|
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
|
// Create the bucket that houses the chain block hash to height
|
||||||
// index.
|
// index.
|
||||||
meta := dbTx.Metadata()
|
_, err = meta.CreateBucket(hashIndexBucketName)
|
||||||
_, err := meta.CreateBucket(hashIndexBucketName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1120,7 +1139,7 @@ func (b *BlockChain) createChainState() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the genesis block into the database.
|
// Store the genesis block into the database.
|
||||||
return dbTx.StoreBlock(genesisBlock)
|
return dbStoreBlock(dbTx, genesisBlock)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1273,6 +1292,60 @@ func dbFetchBlockByNode(dbTx database.Tx, node *blockNode) (*btcutil.Block, erro
|
||||||
return block, nil
|
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.
|
// BlockByHeight returns the block at the given height in the main chain.
|
||||||
//
|
//
|
||||||
// This function is safe for concurrent access.
|
// This function is safe for concurrent access.
|
||||||
|
|
Loading…
Reference in a new issue