prysm-pulse/beacon-chain/db/block.go

172 lines
4.4 KiB
Go
Raw Normal View History

2018-10-05 17:14:50 +00:00
package db
import (
"errors"
"fmt"
2018-10-17 06:11:24 +00:00
"github.com/boltdb/bolt"
2018-10-05 17:14:50 +00:00
"github.com/gogo/protobuf/proto"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
2018-10-05 17:14:50 +00:00
)
func createBlock(enc []byte) (*pb.BeaconBlock, error) {
2018-10-17 06:11:24 +00:00
protoBlock := &pb.BeaconBlock{}
err := proto.Unmarshal(enc, protoBlock)
2018-10-05 17:14:50 +00:00
if err != nil {
2018-10-17 06:11:24 +00:00
return nil, fmt.Errorf("failed to unmarshal encoding: %v", err)
2018-10-05 17:14:50 +00:00
}
return protoBlock, nil
2018-10-17 06:11:24 +00:00
}
// GetBlock accepts a block hash and returns the corresponding block.
// Returns nil if the block does not exist.
func (db *BeaconDB) GetBlock(hash [32]byte) (*pb.BeaconBlock, error) {
var block *pb.BeaconBlock
2018-10-17 06:11:24 +00:00
err := db.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(blockBucket)
2018-10-17 06:11:24 +00:00
enc := bucket.Get(hash[:])
2018-10-17 06:11:24 +00:00
if enc == nil {
return nil
}
var err error
block, err = createBlock(enc)
return err
})
return block, err
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
// HasBlock accepts a block hash and returns true if the block does not exist.
func (db *BeaconDB) HasBlock(hash [32]byte) bool {
hasBlock := false
// #nosec G104
2018-10-17 06:11:24 +00:00
_ = db.view(func(tx *bolt.Tx) error {
bucket := tx.Bucket(blockBucket)
2018-10-17 06:11:24 +00:00
hasBlock = bucket.Get(hash[:]) != nil
2018-10-17 06:11:24 +00:00
return nil
})
return hasBlock
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
// SaveBlock accepts a block and writes it to disk.
func (db *BeaconDB) SaveBlock(block *pb.BeaconBlock) error {
hash, err := b.Hash(block)
2018-10-05 17:14:50 +00:00
if err != nil {
2018-10-17 06:11:24 +00:00
return fmt.Errorf("failed to hash block: %v", err)
2018-10-05 17:14:50 +00:00
}
enc, err := proto.Marshal(block)
2018-10-05 17:14:50 +00:00
if err != nil {
2018-10-17 06:11:24 +00:00
return fmt.Errorf("failed to encode block: %v", err)
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
return db.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(blockBucket)
2018-10-17 06:11:24 +00:00
return bucket.Put(hash[:], enc)
2018-10-17 06:11:24 +00:00
})
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
// GetChainHead returns the head of the main chain.
func (db *BeaconDB) GetChainHead() (*pb.BeaconBlock, error) {
var block *pb.BeaconBlock
2018-10-17 06:11:24 +00:00
err := db.view(func(tx *bolt.Tx) error {
chainInfo := tx.Bucket(chainInfoBucket)
mainChain := tx.Bucket(mainChainBucket)
blockBkt := tx.Bucket(blockBucket)
height := chainInfo.Get(mainChainHeightKey)
if height == nil {
2018-12-01 22:09:12 +00:00
return errors.New("unable to determine chain height")
2018-10-17 06:11:24 +00:00
}
blockhash := mainChain.Get(height)
if blockhash == nil {
return fmt.Errorf("hash at the current height not found: %d", height)
}
enc := blockBkt.Get(blockhash)
if enc == nil {
return fmt.Errorf("block not found: %x", blockhash)
}
var err error
block, err = createBlock(enc)
2018-10-05 17:14:50 +00:00
return err
2018-10-17 06:11:24 +00:00
})
2018-10-05 17:14:50 +00:00
2018-10-17 06:11:24 +00:00
return block, err
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
// UpdateChainHead atomically updates the head of the chain as well as the corresponding state changes
// Including a new crystallized state is optional.
func (db *BeaconDB) UpdateChainHead(block *pb.BeaconBlock, beaconState *pb.BeaconState) error {
blockHash, err := b.Hash(block)
2018-10-05 17:14:50 +00:00
if err != nil {
2018-10-17 06:11:24 +00:00
return fmt.Errorf("unable to get the block hash: %v", err)
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
beaconStateEnc, err := proto.Marshal(beaconState)
2018-10-05 17:14:50 +00:00
if err != nil {
2018-12-01 22:09:12 +00:00
return fmt.Errorf("unable to encode the beacon state: %v", err)
2018-10-17 06:11:24 +00:00
}
2018-10-05 17:14:50 +00:00
slotBinary := encodeSlotNumber(block.GetSlot())
2018-10-05 17:14:50 +00:00
2018-10-17 06:11:24 +00:00
return db.update(func(tx *bolt.Tx) error {
blockBucket := tx.Bucket(blockBucket)
chainInfo := tx.Bucket(chainInfoBucket)
mainChain := tx.Bucket(mainChainBucket)
2018-10-05 17:14:50 +00:00
2018-12-01 22:09:12 +00:00
if blockBucket.Get(blockHash[:]) == nil {
return fmt.Errorf("expected block %#x to have already been saved before updating head: %v", blockHash, err)
2018-10-17 06:11:24 +00:00
}
2018-10-05 17:14:50 +00:00
2018-12-01 22:09:12 +00:00
if err := mainChain.Put(slotBinary, blockHash[:]); err != nil {
2018-10-17 06:11:24 +00:00
return fmt.Errorf("failed to include the block in the main chain bucket: %v", err)
}
if err := chainInfo.Put(mainChainHeightKey, slotBinary); err != nil {
return fmt.Errorf("failed to record the block as the head of the main chain: %v", err)
}
2018-12-01 22:09:12 +00:00
if err := chainInfo.Put(stateLookupKey, beaconStateEnc); err != nil {
return fmt.Errorf("failed to save beacon state as canonical: %v", err)
2018-10-17 06:11:24 +00:00
}
return nil
})
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
// GetBlockBySlot accepts a slot number and returns the corresponding block in the main chain.
// Returns nil if a block was not recorded for the given slot.
func (db *BeaconDB) GetBlockBySlot(slot uint64) (*pb.BeaconBlock, error) {
var block *pb.BeaconBlock
2018-10-17 06:11:24 +00:00
slotEnc := encodeSlotNumber(slot)
2018-10-05 17:14:50 +00:00
2018-10-17 06:11:24 +00:00
err := db.view(func(tx *bolt.Tx) error {
mainChain := tx.Bucket(mainChainBucket)
blockBkt := tx.Bucket(blockBucket)
2018-10-05 17:14:50 +00:00
2018-10-17 06:11:24 +00:00
blockhash := mainChain.Get(slotEnc)
if blockhash == nil {
return nil
}
enc := blockBkt.Get(blockhash)
if enc == nil {
return fmt.Errorf("block not found: %x", blockhash)
}
var err error
block, err = createBlock(enc)
return err
})
2018-10-05 17:14:50 +00:00
return block, err
}