prysm-pulse/beacon-chain/blockchain/core.go
2018-10-02 13:34:26 -05:00

278 lines
8.0 KiB
Go

package blockchain
import (
"errors"
"fmt"
"sync"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/gogo/protobuf/proto"
"github.com/prysmaticlabs/prysm/beacon-chain/types"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
)
// BeaconChain represents the core PoS blockchain object containing
// both a crystallized and active state.
type BeaconChain struct {
state *beaconState
lock sync.Mutex
db ethdb.Database
}
type beaconState struct {
// ActiveState captures the beacon state at block processing level,
// it focuses on verifying aggregated signatures and pending attestations.
ActiveState *types.ActiveState
// CrystallizedState captures the beacon state at cycle transition level,
// it focuses on changes to the validator set, processing cross links and
// setting up FFG checkpoints.
CrystallizedState *types.CrystallizedState
}
// NewBeaconChain initializes a beacon chain using genesis state parameters if
// none provided.
func NewBeaconChain(genesisJSON string, db ethdb.Database) (*BeaconChain, error) {
beaconChain := &BeaconChain{
db: db,
state: &beaconState{},
}
hasCrystallized, err := db.Has(crystallizedStateLookupKey)
if err != nil {
return nil, err
}
hasGenesis, err := db.Has(genesisLookupKey)
if err != nil {
return nil, err
}
active := types.NewGenesisActiveState()
crystallized, err := types.NewGenesisCrystallizedState(genesisJSON)
if err != nil {
return nil, err
}
beaconChain.state.ActiveState = active
if !hasGenesis {
log.Info("No genesis block found on disk, initializing genesis block")
// Active state hash is predefined so error can be safely ignored
// #nosec G104
activeStateHash, _ := active.Hash()
// Crystallized state hash is predefined so error can be safely ignored
// #nosec G104
crystallizedStateHash, _ := crystallized.Hash()
genesisBlock := types.NewGenesisBlock(activeStateHash, crystallizedStateHash)
genesisMarshall, err := proto.Marshal(genesisBlock.Proto())
if err != nil {
return nil, err
}
if err := beaconChain.db.Put(genesisLookupKey, genesisMarshall); err != nil {
return nil, err
}
if err := beaconChain.saveBlock(genesisBlock); err != nil {
return nil, err
}
}
if !hasCrystallized {
log.Info("No chainstate found on disk, initializing beacon from genesis")
beaconChain.state.CrystallizedState = crystallized
return beaconChain, nil
}
enc, err := db.Get(crystallizedStateLookupKey)
if err != nil {
return nil, err
}
crystallizedData := &pb.CrystallizedState{}
err = proto.Unmarshal(enc, crystallizedData)
if err != nil {
return nil, err
}
beaconChain.state.CrystallizedState = types.NewCrystallizedState(crystallizedData)
return beaconChain, nil
}
// genesisBlock returns the canonical, genesis block.
func (b *BeaconChain) genesisBlock() (*types.Block, error) {
genesisExists, err := b.db.Has(genesisLookupKey)
if err != nil {
return nil, err
}
if genesisExists {
bytes, err := b.db.Get(genesisLookupKey)
if err != nil {
return nil, err
}
block := &pb.BeaconBlock{}
if err := proto.Unmarshal(bytes, block); err != nil {
return nil, err
}
return types.NewBlock(block), nil
}
active := types.NewGenesisActiveState()
// Active state hash is predefined so error can be safely ignored
// #nosec G104
activeStateHash, _ := active.Hash()
// Crystallized state hash is predefined so error can be safely ignored
// #nosec G104
crystallizedStateHash, _ := b.CrystallizedState().Hash()
return types.NewGenesisBlock(activeStateHash, crystallizedStateHash), nil
}
// CanonicalHead fetches the latest head stored in persistent storage.
func (b *BeaconChain) CanonicalHead() (*types.Block, error) {
has, err := b.db.Has(canonicalHeadLookupKey)
if err != nil {
return nil, err
}
// If there has not been a canonical head stored yet, we
// return the genesis block of the chain.
if !has {
return b.genesisBlock()
}
bytes, err := b.db.Get(canonicalHeadLookupKey)
if err != nil {
return nil, err
}
block := &pb.BeaconBlock{}
if err := proto.Unmarshal(bytes, block); err != nil {
return nil, fmt.Errorf("cannot unmarshal proto: %v", err)
}
return types.NewBlock(block), nil
}
// ActiveState contains the current state of attestations and changes every block.
func (b *BeaconChain) ActiveState() *types.ActiveState {
return b.state.ActiveState
}
// CrystallizedState contains cycle dependent validator information, changes every cycle.
func (b *BeaconChain) CrystallizedState() *types.CrystallizedState {
return b.state.CrystallizedState
}
// SetActiveState is a convenience method which sets and persists the active state on the beacon chain.
func (b *BeaconChain) SetActiveState(activeState *types.ActiveState) error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.ActiveState = activeState
return b.PersistActiveState()
}
// SetCrystallizedState is a convenience method which sets and persists the crystallized state on the beacon chain.
func (b *BeaconChain) SetCrystallizedState(crystallizedState *types.CrystallizedState) error {
b.lock.Lock()
defer b.lock.Unlock()
b.state.CrystallizedState = crystallizedState
return b.PersistCrystallizedState()
}
// PersistActiveState stores proto encoding of the current beacon chain active state into the db.
func (b *BeaconChain) PersistActiveState() error {
encodedState, err := b.ActiveState().Marshal()
if err != nil {
return err
}
return b.db.Put(activeStateLookupKey, encodedState)
}
// PersistCrystallizedState stores proto encoding of the current beacon chain crystallized state into the db.
func (b *BeaconChain) PersistCrystallizedState() error {
encodedState, err := b.CrystallizedState().Marshal()
if err != nil {
return err
}
return b.db.Put(crystallizedStateLookupKey, encodedState)
}
func (b *BeaconChain) hasBlock(blockhash [32]byte) (bool, error) {
return b.db.Has(blockKey(blockhash))
}
// saveBlock puts the passed block into the beacon chain db.
func (b *BeaconChain) saveBlock(block *types.Block) error {
hash, err := block.Hash()
if err != nil {
return err
}
key := blockKey(hash)
encodedState, err := block.Marshal()
if err != nil {
return err
}
return b.db.Put(key, encodedState)
}
// saveCanonicalSlotNumber saves the slotnumber and blockhash of a canonical block
// saved in the db. This will alow for canonical blocks to be retrieved from the db
// by using their slotnumber as a key, and then using the retrieved blockhash to get
// the block from the db.
// prefix + slotNumber -> blockhash
// prefix + blockHash -> block
func (b *BeaconChain) saveCanonicalSlotNumber(slotNumber uint64, hash [32]byte) error {
return b.db.Put(canonicalBlockKey(slotNumber), hash[:])
}
// saveCanonicalBlock puts the passed block into the beacon chain db
// and also saves a "latest-head" key mapping to the block in the db.
func (b *BeaconChain) saveCanonicalBlock(block *types.Block) error {
enc, err := block.Marshal()
if err != nil {
return err
}
return b.db.Put(canonicalHeadLookupKey, enc)
}
// getBlock retrieves a block from the db using its hash.
func (b *BeaconChain) getBlock(hash [32]byte) (*types.Block, error) {
key := blockKey(hash)
has, err := b.db.Has(key)
if err != nil {
return nil, err
}
if !has {
return nil, errors.New("block not found")
}
enc, err := b.db.Get(key)
if err != nil {
return nil, err
}
block := &pb.BeaconBlock{}
err = proto.Unmarshal(enc, block)
return types.NewBlock(block), err
}
// removeBlock removes the block from the db.
func (b *BeaconChain) removeBlock(hash [32]byte) error {
return b.db.Delete(blockKey(hash))
}
// hasCanonicalBlockForSlot checks the db if the canonical block for
// this slot exists.
func (b *BeaconChain) hasCanonicalBlockForSlot(slotNumber uint64) (bool, error) {
return b.db.Has(canonicalBlockKey(slotNumber))
}
// canonicalBlockForSlot retrieves the canonical block which is saved in the db
// for that required slot number.
func (b *BeaconChain) canonicalBlockForSlot(slotNumber uint64) (*types.Block, error) {
enc, err := b.db.Get(canonicalBlockKey(slotNumber))
if err != nil {
return nil, err
}
var blockhash [32]byte
copy(blockhash[:], enc)
block, err := b.getBlock(blockhash)
return block, err
}