2018-10-05 17:14:50 +00:00
|
|
|
package db
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
2018-10-10 16:17:48 +00:00
|
|
|
"time"
|
2018-10-05 17:14:50 +00:00
|
|
|
|
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"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/types"
|
|
|
|
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
|
|
|
)
|
|
|
|
|
2018-10-17 06:11:24 +00:00
|
|
|
func createBlock(enc []byte) (*types.Block, error) {
|
|
|
|
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
|
|
|
}
|
2018-10-17 06:11:24 +00:00
|
|
|
|
|
|
|
block := types.NewBlock(protoBlock)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to instantiate a block from the encoding: %v", err)
|
2018-10-05 17:14:50 +00:00
|
|
|
}
|
2018-10-17 06:11:24 +00:00
|
|
|
|
|
|
|
return block, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) (*types.Block, error) {
|
|
|
|
var block *types.Block
|
|
|
|
err := db.view(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(blockBucket)
|
|
|
|
|
|
|
|
enc := b.Get(hash[:])
|
|
|
|
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
|
2018-10-30 16:00:20 +00:00
|
|
|
// #nosec G104
|
2018-10-17 06:11:24 +00:00
|
|
|
_ = db.view(func(tx *bolt.Tx) error {
|
|
|
|
b := tx.Bucket(blockBucket)
|
|
|
|
|
|
|
|
hasBlock = b.Get(hash[:]) != nil
|
|
|
|
|
|
|
|
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.
|
2018-10-05 17:14:50 +00:00
|
|
|
func (db *BeaconDB) SaveBlock(block *types.Block) error {
|
|
|
|
hash, err := block.Hash()
|
|
|
|
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
|
|
|
}
|
2018-10-17 06:11:24 +00:00
|
|
|
enc, err := block.Marshal()
|
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 {
|
|
|
|
b := tx.Bucket(blockBucket)
|
|
|
|
|
|
|
|
return b.Put(hash[:], enc)
|
|
|
|
})
|
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() (*types.Block, error) {
|
|
|
|
var block *types.Block
|
|
|
|
|
|
|
|
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 {
|
|
|
|
return errors.New("unable to determinechain height")
|
|
|
|
}
|
|
|
|
|
|
|
|
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 *types.Block, aState *types.ActiveState, cState *types.CrystallizedState) error {
|
|
|
|
blockhash, err := block.Hash()
|
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
|
|
|
|
|
|
|
aStateEnc, err := aState.Marshal()
|
2018-10-05 17:14:50 +00:00
|
|
|
if err != nil {
|
2018-10-17 06:11:24 +00:00
|
|
|
return fmt.Errorf("unable to encode the active state: %v", err)
|
2018-10-05 17:14:50 +00:00
|
|
|
}
|
|
|
|
|
2018-10-17 06:11:24 +00:00
|
|
|
var cStateEnc []byte
|
|
|
|
if cState != nil {
|
|
|
|
cStateEnc, err = cState.Marshal()
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("unable to encode the crystallized state: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2018-10-05 17:14:50 +00:00
|
|
|
|
2018-10-17 06:11:24 +00:00
|
|
|
slotBinary := encodeSlotNumber(block.SlotNumber())
|
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-10-17 06:11:24 +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-05 17:14:50 +00:00
|
|
|
|
2018-10-17 06:11:24 +00:00
|
|
|
if err := mainChain.Put(slotBinary, blockhash[:]); err != nil {
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := chainInfo.Put(aStateLookupKey, aStateEnc); err != nil {
|
|
|
|
return fmt.Errorf("failed to save active state as canonical: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if cStateEnc != nil {
|
|
|
|
if err := chainInfo.Put(cStateLookupKey, cStateEnc); err != nil {
|
|
|
|
return fmt.Errorf("failed to save crystallized state as canonical: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) (*types.Block, error) {
|
|
|
|
var block *types.Block
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2018-10-10 16:17:48 +00:00
|
|
|
// GetGenesisTime returns the timestamp for the genesis block
|
|
|
|
func (db *BeaconDB) GetGenesisTime() (time.Time, error) {
|
2018-10-17 06:11:24 +00:00
|
|
|
genesis, err := db.GetBlockBySlot(0)
|
2018-10-10 16:17:48 +00:00
|
|
|
if err != nil {
|
2018-10-31 22:05:55 +00:00
|
|
|
return time.Time{}, fmt.Errorf("could not get genesis block: %v", err)
|
2018-10-10 16:17:48 +00:00
|
|
|
}
|
2018-10-17 06:11:24 +00:00
|
|
|
if genesis == nil {
|
2018-10-31 22:05:55 +00:00
|
|
|
return time.Time{}, fmt.Errorf("genesis block not found: %v", err)
|
2018-10-17 06:11:24 +00:00
|
|
|
}
|
|
|
|
|
2018-10-10 16:17:48 +00:00
|
|
|
genesisTime, err := genesis.Timestamp()
|
|
|
|
if err != nil {
|
2018-10-31 22:05:55 +00:00
|
|
|
return time.Time{}, fmt.Errorf("could not get genesis timestamp: %v", err)
|
2018-10-10 16:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return genesisTime, nil
|
|
|
|
}
|