prysm-pulse/beacon-chain/blockchain/stategenerator/state_generator.go
2019-03-20 15:33:31 -07:00

149 lines
4.1 KiB
Go

package stategenerator
import (
"context"
"fmt"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
"github.com/prysmaticlabs/prysm/beacon-chain/db"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/sirupsen/logrus"
)
var log = logrus.WithField("prefix", "stategenerator")
// GenerateStateFromBlock generates state from the last finalized state to the input slot.
// Ex:
// 1A - 2B(finalized) - 3C - 4 - 5D - 6 - 7F (letters mean there's a block)
// Input: slot 6
// Output: resulting state of state transition function after applying block C and D
// along with skipped slot 4 and 6.
func GenerateStateFromBlock(ctx context.Context, db *db.BeaconDB, slot uint64) (*pb.BeaconState, error) {
fState, err := db.FinalizedState()
if err != nil {
return nil, err
}
// return finalized state if it's the same as input slot.
if fState.Slot == slot {
return fState, nil
}
// input slot can't be smaller than last finalized state's slot.
if fState.Slot > slot {
return nil, fmt.Errorf(
"requested slot %d < current slot %d in the finalized beacon state",
fState.Slot,
slot,
)
}
fBlock, err := db.FinalizedBlock()
if err != nil {
return nil, err
}
fRoot, err := hashutil.HashBeaconBlock(fBlock)
if err != nil {
return nil, fmt.Errorf("unable to get block root %v", err)
}
// from input slot, retrieve its corresponding block and call that the most recent block.
mostRecentBlock, err := db.BlockBySlot(slot)
if err != nil {
return nil, err
}
// if the most recent block is a skip block, we get its parent block.
// ex:
// 1A - 2B - 3C - 4 - 5 (letters mean there's a block)
// input slot is 5, but slots 4 and 5 are skipped, we get block C from slot 3.
for mostRecentBlock == nil {
slot--
mostRecentBlock, err = db.BlockBySlot(slot)
if err != nil {
return nil, err
}
}
// retrieve the block list to recompute state of the input slot.
blocks, err := blocksSinceFinalized(db, mostRecentBlock, fRoot)
if err != nil {
return nil, fmt.Errorf("unable to look up block ancestors %v", err)
}
log.Debugf("Recompute state starting last finalized slot %d and ending slot %d",
fState.Slot, slot)
postState := fState
root := fRoot
for i := len(blocks); i > 0; i-- {
block := blocks[i-1]
if block.Slot <= postState.Slot {
continue
}
// Running state transitions for skipped slots.
for block.Slot != fState.Slot+1 {
postState, err = state.ExecuteStateTransition(
ctx,
postState,
nil,
root,
&state.TransitionConfig{
VerifySignatures: true,
Logging: false,
},
)
if err != nil {
return nil, fmt.Errorf("could not execute state transition %v", err)
}
}
postState, err = state.ExecuteStateTransition(
ctx,
postState,
block,
root,
&state.TransitionConfig{
VerifySignatures: true,
Logging: false,
},
)
if err != nil {
return nil, fmt.Errorf("could not execute state transition %v", err)
}
root, err = hashutil.HashBeaconBlock(block)
if err != nil {
return nil, fmt.Errorf("unable to get block root %v", err)
}
}
log.Debugf("Finished recompute state with slot %d and finalized epoch %d",
postState.Slot, postState.FinalizedEpoch)
return postState, nil
}
// blocksSinceFinalized will return a list of linked blocks that's
// between the input block and the last finalized block in the db.
// The input block is also returned in the list.
// Ex:
// A -> B(finalized) -> C -> D -> E -> D
// Input: E, output: [E, D, C, B]
func blocksSinceFinalized(db *db.BeaconDB, block *pb.BeaconBlock,
finalizedBlockRoot [32]byte) ([]*pb.BeaconBlock, error) {
blockAncestors := make([]*pb.BeaconBlock, 0)
blockAncestors = append(blockAncestors, block)
parentRoot := bytesutil.ToBytes32(block.ParentRootHash32)
// looking up ancestors, until the finalized block.
for parentRoot != finalizedBlockRoot {
retblock, err := db.Block(parentRoot)
if err != nil {
return nil, err
}
blockAncestors = append(blockAncestors, retblock)
parentRoot = bytesutil.ToBytes32(retblock.ParentRootHash32)
}
return blockAncestors, nil
}