Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compute state up to slot #5035

Merged
merged 9 commits into from
Mar 8, 2020
47 changes: 47 additions & 0 deletions beacon-chain/state/stategen/replay.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,53 @@ import (
"go.opencensus.io/trace"
)

// ComputeStateUpToSlot returns a processed state up to input target slot.
// If the last processed block is at slot 32, given input target slot at 40, this
// returns processed state up to slot 40 via empty slots.
// If there's duplicated blocks in a single slot, the canonical block will be returned.
func (s *State) ComputeStateUpToSlot(ctx context.Context, targetSlot uint64) (*state.BeaconState, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok , what if you have two blocks at slot 30 ? how would you know which state to return ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It returns the canonical one (ie ancestor of the canonical chain from the node's point of view)
Will update the comment

ctx, span := trace.StartSpan(ctx, "stateGen.ComputeStateUpToSlot")
defer span.End()

// Return genesis state if target slot is 0.
if targetSlot == 0 {
return s.beaconDB.GenesisState(ctx)
}

lastBlockRoot, lastBlockSlot, err := s.lastSavedBlock(ctx, targetSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get last saved block")
}

lastBlockRootForState, err := s.lastSavedState(ctx, targetSlot)
if err != nil {
return nil, errors.Wrap(err, "could not get last valid state")
}
lastState, err := s.beaconDB.State(ctx, lastBlockRootForState)
if err != nil {
return nil, err
}
if lastState == nil {
return nil, errUnknownState
}

// Return if the last valid state's slot is higher than the target slot.
if lastState.Slot() >= targetSlot {
return lastState, nil
}

blks, err := s.LoadBlocks(ctx, lastState.Slot()+1, lastBlockSlot, lastBlockRoot)
if err != nil {
return nil, errors.Wrap(err, "could not load blocks")
}
lastState, err = s.ReplayBlocks(ctx, lastState, blks, targetSlot)
if err != nil {
return nil, errors.Wrap(err, "could not replay blocks")
}

return lastState, nil
}

// ReplayBlocks replays the input blocks on the input state until the target slot is reached.
func (s *State) ReplayBlocks(ctx context.Context, state *state.BeaconState, signed []*ethpb.SignedBeaconBlock, targetSlot uint64) (*state.BeaconState, error) {
var err error
Expand Down
65 changes: 65 additions & 0 deletions beacon-chain/state/stategen/replay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"reflect"
"testing"

"github.com/gogo/protobuf/proto"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
"github.com/prysmaticlabs/go-ssz"
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
Expand All @@ -17,6 +18,70 @@ import (
"github.com/prysmaticlabs/prysm/shared/testutil"
)

func TestComputeStateUpToSlot_GenesisState(t *testing.T) {
ctx := context.Background()
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)

service := New(db)

gBlk := &ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{}}
gRoot, err := ssz.HashTreeRoot(gBlk.Block)
if err != nil {
t.Fatal(err)
}
if err := service.beaconDB.SaveBlock(ctx, gBlk); err != nil {
t.Fatal(err)
}
if err := service.beaconDB.SaveGenesisBlockRoot(ctx, gRoot); err != nil {
t.Fatal(err)
}
beaconState, _ := testutil.DeterministicGenesisState(t, 32)
if err := service.beaconDB.SaveState(ctx, beaconState, gRoot); err != nil {
t.Fatal(err)
}

s, err := service.ComputeStateUpToSlot(ctx, 0)
if err != nil {
t.Fatal(err)
}

if !proto.Equal(s.InnerStateUnsafe(), beaconState.InnerStateUnsafe()) {
t.Error("Did not receive correct genesis state")
}
}

func TestComputeStateUpToSlot_CanProcessUpTo(t *testing.T) {
ctx := context.Background()
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)

service := New(db)

gBlk := &ethpb.SignedBeaconBlock{Block: &ethpb.BeaconBlock{}}
gRoot, err := ssz.HashTreeRoot(gBlk.Block)
if err != nil {
t.Fatal(err)
}
if err := service.beaconDB.SaveBlock(ctx, gBlk); err != nil {
t.Fatal(err)
}
beaconState, _ := testutil.DeterministicGenesisState(t, 32)
if err := service.beaconDB.SaveState(ctx, beaconState, gRoot); err != nil {
t.Fatal(err)
}

s, err := service.ComputeStateUpToSlot(ctx, params.BeaconConfig().SlotsPerEpoch+1)
if err != nil {
t.Fatal(err)
}

if s.Slot() != params.BeaconConfig().SlotsPerEpoch+1 {
t.Log(s.Slot())
t.Error("Did not receive correct processed state")
}
}

func TestReplayBlocks_AllSkipSlots(t *testing.T) {
db := testDB.SetupDB(t)
defer testDB.TeardownDB(t, db)
Expand Down