prysm-pulse/beacon-chain/blockchain/stategenerator/state_generator.go
Raul Jordan a4128f691b
Refactor DB Package to Enable Multiple Blocks/States at Slots (#2540)
* prefixed blocks blocked

* db refactor

* new historical state saving

* builds but tests fail

* more tests pass

* fix tests

* fix tests

* delete buf

* Update beacon-chain/db/block.go

Co-Authored-By: rauljordan <raul@prysmaticlabs.com>

* Update beacon-chain/db/block.go

Co-Authored-By: rauljordan <raul@prysmaticlabs.com>

* rem unused
2019-05-09 10:42:12 -05:00

180 lines
5.6 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/prysmaticlabs/prysm/shared/params"
"github.com/sirupsen/logrus"
"go.opencensus.io/trace"
)
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) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.stategenerator.GenerateStateFromBlock")
defer span.End()
fState, err := db.HistoricalStateFromSlot(ctx, slot, [32]byte{})
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",
slot-params.BeaconConfig().GenesisSlot,
fState.Slot-params.BeaconConfig().GenesisSlot,
)
}
if fState.LatestBlock == nil {
return nil, fmt.Errorf("latest head in state is nil %v", err)
}
fRoot, err := hashutil.HashBeaconBlock(fState.LatestBlock)
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.
mostRecentBlocks, err := db.BlocksBySlot(ctx, slot)
if err != nil {
return nil, err
}
mostRecentBlock := mostRecentBlocks[0]
// 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.
lastSlot := slot
for mostRecentBlock == nil {
lastSlot--
blocks, err := db.BlocksBySlot(ctx, lastSlot)
if err != nil {
return nil, err
}
mostRecentBlock = blocks[0]
}
// retrieve the block list to recompute state of the input slot.
blocks, err := blocksSinceFinalized(ctx, db, mostRecentBlock, fRoot)
if err != nil {
return nil, fmt.Errorf("unable to look up block ancestors %v", err)
}
log.Infof("Recompute state starting last finalized slot %d and ending slot %d",
fState.Slot-params.BeaconConfig().GenesisSlot, slot-params.BeaconConfig().GenesisSlot)
postState := fState
root := fRoot
// this recomputes state up to the last available block.
// ex: 1A - 2B (finalized) - 3C - 4 - 5 - 6C - 7 - 8 (C is the last block).
// input slot 8, this recomputes state to slot 6.
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: false,
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: false,
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)
}
}
// this recomputes state from last block to last slot if there's skipp slots after.
// ex: 1A - 2B (finalized) - 3C - 4 - 5 - 6C - 7 - 8 (7 and 8 are skipped slots).
// input slot 8, this recomputes state from 6C to 8.
for i := postState.Slot; i < slot; i++ {
postState, err = state.ExecuteStateTransition(
ctx,
postState,
nil,
root,
&state.TransitionConfig{
VerifySignatures: false,
Logging: false,
},
)
if err != nil {
return nil, fmt.Errorf("could not execute state transition %v", err)
}
}
log.Infof("Finished recompute state with slot %d and finalized epoch %d",
postState.Slot-params.BeaconConfig().GenesisSlot, postState.FinalizedEpoch-params.BeaconConfig().GenesisEpoch)
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(ctx context.Context, db *db.BeaconDB, block *pb.BeaconBlock,
finalizedBlockRoot [32]byte) ([]*pb.BeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.blockchain.stategenerator.blocksSinceFinalized")
defer span.End()
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
}