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

304 lines
8.5 KiB
Go
Raw Normal View History

2018-10-05 17:14:50 +00:00
package db
import (
"context"
2018-10-05 17:14:50 +00:00
"errors"
"fmt"
"github.com/boltdb/bolt"
"github.com/gogo/protobuf/proto"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"go.opencensus.io/trace"
)
var (
badBlockCount = promauto.NewCounter(prometheus.CounterOpts{
Name: "bad_blocks",
Help: "Number of bad, blacklisted blocks received",
})
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
}
// Block accepts a block root and returns the corresponding block.
2018-10-17 06:11:24 +00:00
// Returns nil if the block does not exist.
func (db *BeaconDB) Block(root [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(root[:])
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
}
// HasBlock accepts a block root and returns true if the block does not exist.
func (db *BeaconDB) HasBlock(root [32]byte) bool {
2018-10-17 06:11:24 +00:00
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(root[:]) != nil
2018-10-17 06:11:24 +00:00
return nil
})
return hasBlock
2018-10-05 17:14:50 +00:00
}
// IsEvilBlockHash determines if a certain block root has been blacklisted
// due to failing to process core state transitions.
func (db *BeaconDB) IsEvilBlockHash(root [32]byte) bool {
db.badBlocksLock.Lock()
defer db.badBlocksLock.Unlock()
if db.badBlockHashes != nil {
return db.badBlockHashes[root]
}
db.badBlockHashes = make(map[[32]byte]bool)
return false
}
// MarkEvilBlockHash makes a block hash as tainted because it corresponds
// to a block which fails core state transition processing.
func (db *BeaconDB) MarkEvilBlockHash(root [32]byte) {
db.badBlocksLock.Lock()
defer db.badBlocksLock.Unlock()
if db.badBlockHashes == nil {
db.badBlockHashes = make(map[[32]byte]bool)
}
db.badBlockHashes[root] = true
badBlockCount.Inc()
}
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 {
root, err := hashutil.HashBeaconBlock(block)
2018-10-05 17:14:50 +00:00
if err != nil {
return fmt.Errorf("failed to tree 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
}
Allow 8 Validator Multinode Cluster to Run Indefinitely (#2050) * plug forkchoice to blockchain service's block processing * fixed tests * more fixes... * clean ups * fixed test * Update beacon-chain/blockchain/block_processing.go * merged with 2006 and started fixing tests * remove prints * fixed tests * lint * include ops service * if there's a skip slot, slot-- * fixed typo * started working on test * no fork choice in propose * bleh, need to fix state generator first * state gen takes input slot * feedback * fixed tests * preston's feedback * fmt * removed extra logging * add more logs * fixed validator attest * builds * fixed save block * children fix * removed verbose logs * fix fork choice * right logs * Add Prometheus Counter for Reorg (#2051) * fetch every slot (#2052) * test Fixes * lint * only regenerate state if there was a reorg * better logging * fixed seed * better logging * process skip slots in assignment requests * fix lint * disable state root computation * filter attestations in regular sync * log important items * better info logs * added spans to stategen * span in stategen * set validator deadline * randao stuff * disable sig verify * lint * lint * save only using historical states * use new goroutine for handling sync messages * change default buffer sizes * better p2p * rem some useless logs * lint * sync tests complete * complete tests * tests fixed * lint * fix flakey att service * PR feedback * undo k8s changes * Update beacon-chain/blockchain/block_processing.go * Update beacon-chain/sync/regular_sync.go * Add feature flag to enable compute state root * add comment * gazelle lint fix
2019-03-25 15:21:21 +00:00
slotBinary := encodeSlotNumber(block.Slot)
2018-10-05 17:14:50 +00:00
if block.Slot > db.highestBlockSlot {
db.highestBlockSlot = block.Slot
}
2018-10-17 06:11:24 +00:00
return db.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(blockBucket)
Allow 8 Validator Multinode Cluster to Run Indefinitely (#2050) * plug forkchoice to blockchain service's block processing * fixed tests * more fixes... * clean ups * fixed test * Update beacon-chain/blockchain/block_processing.go * merged with 2006 and started fixing tests * remove prints * fixed tests * lint * include ops service * if there's a skip slot, slot-- * fixed typo * started working on test * no fork choice in propose * bleh, need to fix state generator first * state gen takes input slot * feedback * fixed tests * preston's feedback * fmt * removed extra logging * add more logs * fixed validator attest * builds * fixed save block * children fix * removed verbose logs * fix fork choice * right logs * Add Prometheus Counter for Reorg (#2051) * fetch every slot (#2052) * test Fixes * lint * only regenerate state if there was a reorg * better logging * fixed seed * better logging * process skip slots in assignment requests * fix lint * disable state root computation * filter attestations in regular sync * log important items * better info logs * added spans to stategen * span in stategen * set validator deadline * randao stuff * disable sig verify * lint * lint * save only using historical states * use new goroutine for handling sync messages * change default buffer sizes * better p2p * rem some useless logs * lint * sync tests complete * complete tests * tests fixed * lint * fix flakey att service * PR feedback * undo k8s changes * Update beacon-chain/blockchain/block_processing.go * Update beacon-chain/sync/regular_sync.go * Add feature flag to enable compute state root * add comment * gazelle lint fix
2019-03-25 15:21:21 +00:00
mainChain := tx.Bucket(mainChainBucket)
if err := mainChain.Put(slotBinary, root[:]); err != nil {
return fmt.Errorf("failed to include the block in the main chain bucket: %v", err)
}
return bucket.Put(root[:], enc)
2018-10-17 06:11:24 +00:00
})
2018-10-05 17:14:50 +00:00
}
// DeleteBlock deletes a block using the slot and its root as keys in their respective buckets.
func (db *BeaconDB) DeleteBlock(block *pb.BeaconBlock) error {
root, err := hashutil.HashBeaconBlock(block)
if err != nil {
return fmt.Errorf("failed to tree hash block: %v", err)
}
slotBinary := encodeSlotNumber(block.Slot)
return db.update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(blockBucket)
mainChain := tx.Bucket(mainChainBucket)
if err := mainChain.Delete(slotBinary); err != nil {
return fmt.Errorf("failed to include the block in the main chain bucket: %v", err)
}
return bucket.Delete(root[:])
})
}
// SaveJustifiedBlock saves the last justified block from canonical chain to DB.
func (db *BeaconDB) SaveJustifiedBlock(block *pb.BeaconBlock) error {
return db.update(func(tx *bolt.Tx) error {
enc, err := proto.Marshal(block)
if err != nil {
return fmt.Errorf("failed to encode block: %v", err)
}
chainInfo := tx.Bucket(chainInfoBucket)
return chainInfo.Put(justifiedBlockLookupKey, enc)
})
}
// SaveFinalizedBlock saves the last finalized block from canonical chain to DB.
func (db *BeaconDB) SaveFinalizedBlock(block *pb.BeaconBlock) error {
return db.update(func(tx *bolt.Tx) error {
enc, err := proto.Marshal(block)
if err != nil {
return fmt.Errorf("failed to encode block: %v", err)
}
chainInfo := tx.Bucket(chainInfoBucket)
return chainInfo.Put(finalizedBlockLookupKey, enc)
})
}
// JustifiedBlock retrieves the justified block from the db.
func (db *BeaconDB) JustifiedBlock() (*pb.BeaconBlock, error) {
var block *pb.BeaconBlock
err := db.view(func(tx *bolt.Tx) error {
chainInfo := tx.Bucket(chainInfoBucket)
encBlock := chainInfo.Get(justifiedBlockLookupKey)
if encBlock == nil {
return errors.New("no justified block saved")
}
var err error
block, err = createBlock(encBlock)
return err
})
return block, err
}
// FinalizedBlock retrieves the finalized block from the db.
func (db *BeaconDB) FinalizedBlock() (*pb.BeaconBlock, error) {
var block *pb.BeaconBlock
err := db.view(func(tx *bolt.Tx) error {
chainInfo := tx.Bucket(chainInfoBucket)
encBlock := chainInfo.Get(finalizedBlockLookupKey)
if encBlock == nil {
return errors.New("no finalized block saved")
}
var err error
block, err = createBlock(encBlock)
return err
})
return block, err
}
// ChainHead returns the head of the main chain.
func (db *BeaconDB) ChainHead() (*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
}
blockRoot := mainChain.Get(height)
if blockRoot == nil {
return fmt.Errorf("root at the current height not found: %d", height)
2018-10-17 06:11:24 +00:00
}
enc := blockBkt.Get(blockRoot)
2018-10-17 06:11:24 +00:00
if enc == nil {
return fmt.Errorf("block not found: %x", blockRoot)
2018-10-17 06:11:24 +00:00
}
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
2019-03-03 17:31:29 +00:00
// Including a new state is optional.
func (db *BeaconDB) UpdateChainHead(ctx context.Context, block *pb.BeaconBlock, beaconState *pb.BeaconState) error {
ctx, span := trace.StartSpan(ctx, "beacon-chain.db.UpdateChainHead")
defer span.End()
blockRoot, err := hashutil.HashBeaconBlock(block)
2018-10-05 17:14:50 +00:00
if err != nil {
return fmt.Errorf("unable to tree hash block: %v", err)
2018-10-05 17:14:50 +00:00
}
2018-10-17 06:11:24 +00:00
slotBinary := encodeSlotNumber(block.Slot)
if block.Slot > db.highestBlockSlot {
db.highestBlockSlot = block.Slot
}
2018-10-05 17:14:50 +00:00
if err := db.SaveState(ctx, beaconState); err != nil {
return fmt.Errorf("failed to save beacon state as canonical: %v", err)
2019-03-14 06:43:39 +00:00
}
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
if blockBucket.Get(blockRoot[:]) == nil {
return fmt.Errorf("expected block %#x to have already been saved before updating head: %v", blockRoot, err)
2018-10-17 06:11:24 +00:00
}
2018-10-05 17:14:50 +00:00
if err := mainChain.Put(slotBinary, blockRoot[:]); 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)
}
return nil
})
2018-10-05 17:14:50 +00:00
}
// BlockBySlot accepts a slot number and returns the corresponding block in the main chain.
2018-10-17 06:11:24 +00:00
// Returns nil if a block was not recorded for the given slot.
func (db *BeaconDB) BlockBySlot(ctx context.Context, slot uint64) (*pb.BeaconBlock, error) {
_, span := trace.StartSpan(ctx, "BeaconDB.BlockBySlot")
defer span.End()
span.AddAttributes(trace.Int64Attribute("slot", int64(slot-params.BeaconConfig().GenesisSlot)))
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
blockRoot := mainChain.Get(slotEnc)
if blockRoot == nil {
2018-10-17 06:11:24 +00:00
return nil
}
enc := blockBkt.Get(blockRoot)
2018-10-17 06:11:24 +00:00
if enc == nil {
return nil
2018-10-17 06:11:24 +00:00
}
var err error
block, err = createBlock(enc)
return err
})
2018-10-05 17:14:50 +00:00
return block, err
}
// HighestBlockSlot returns the in-memory value for the highest block we've
// seen in the database.
func (db *BeaconDB) HighestBlockSlot() uint64 {
return db.highestBlockSlot
}