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

Replay blocks and generate state without sig verification #4943

Merged
merged 10 commits into from Feb 26, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions beacon-chain/core/state/BUILD.bazel
Expand Up @@ -8,6 +8,7 @@ go_library(
"skip_slot_cache.go",
"state.go",
"transition.go",
"transition_stategen.go",
],
importpath = "github.com/prysmaticlabs/prysm/beacon-chain/core/state",
visibility = [
Expand Down
8 changes: 4 additions & 4 deletions beacon-chain/core/state/transition.go
Expand Up @@ -176,7 +176,7 @@ func CalculateStateRoot(
}

// Execute per block transition.
state, err = computeStateRoot(ctx, state, signed)
state, err = ProcessBlockForStateRoot(ctx, state, signed)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not process block")
}
Expand Down Expand Up @@ -640,9 +640,9 @@ func ProcessEpochPrecompute(ctx context.Context, state *stateTrie.BeaconState) (
return state, nil
}

// computeStateRoot computes the state root of the block without verifying proposer signature
// and randao.
func computeStateRoot(
// ProcessBlockForStateRoot processes the state for state root computation. It skips proposer signature
// and randao signature verifications.
func ProcessBlockForStateRoot(
ctx context.Context,
state *stateTrie.BeaconState,
signed *ethpb.SignedBeaconBlock,
Expand Down
4 changes: 2 additions & 2 deletions beacon-chain/core/state/transition_fuzz_test.go
Expand Up @@ -187,7 +187,7 @@ func TestFuzzProcessEpochPrecompute_1000(t *testing.T) {
}
}

func TestFuzzcomputeStateRoot_1000(t *testing.T) {
func TestFuzzProcessBlockForStateRoot_1000(t *testing.T) {
ctx := context.Background()
state := &stateTrie.BeaconState{}
sb := &ethpb.SignedBeaconBlock{}
Expand All @@ -196,7 +196,7 @@ func TestFuzzcomputeStateRoot_1000(t *testing.T) {
for i := 0; i < 1000; i++ {
fuzzer.Fuzz(state)
fuzzer.Fuzz(sb)
s, err := computeStateRoot(ctx, state, sb)
s, err := ProcessBlockForStateRoot(ctx, state, sb)
if err != nil && s != nil {
t.Fatalf("state should be nil on err. found: %v on error: %v for signed block: %v", s, err, sb)
}
Expand Down
85 changes: 85 additions & 0 deletions beacon-chain/core/state/transition_stategen.go
@@ -0,0 +1,85 @@
package state

import (
"context"
"fmt"

"github.com/pkg/errors"
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
"go.opencensus.io/trace"
)

// ExecuteStateTransitionStateGen applies state transition on input historical state and block for state gen usages.
// There's no signature verification involved given state gen only works with stored block and state in DB.
// If the objects are already in stored in DB, one can omit redundant signature checks and ssz hashing calculations.
// WARNING: This method should not be used for new block.
Copy link
Member

Choose a reason for hiding this comment

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

Can this be private? This warning makes me a bit nervous since it is not enforced

Copy link
Member Author

Choose a reason for hiding this comment

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

Great point. I have moved it to stategen package and made it private

func ExecuteStateTransitionStateGen(
ctx context.Context,
state *stateTrie.BeaconState,
signed *ethpb.SignedBeaconBlock,
) (*stateTrie.BeaconState, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
if signed == nil || signed.Block == nil {
return nil, errors.New("nil block")
}

ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ExecuteStateTransitionStateGen")
defer span.End()
var err error

// Execute per slots transition.
// Given this is for state gen, a node uses the version process slots without skip slots cache.
state, err = ProcessSlotsStateGen(ctx, state, signed.Block.Slot)
if err != nil {
return nil, errors.Wrap(err, "could not process slot")
}

// Execute per block transition.
// Given this is for state gen, a node only cares about the post state without proposer
// and randao signature verifications.
state, err = ProcessBlockForStateRoot(ctx, state, signed)
if err != nil {
return nil, errors.Wrap(err, "could not process block")
}

return state, nil
}

// ProcessSlotsStateGen to process old slots for state gen usages.
// There's no skip slot cache involved given state gen only works with already stored block and state in DB.
// WARNING: This method should not be used for new slot.
func ProcessSlotsStateGen(ctx context.Context, state *stateTrie.BeaconState, slot uint64) (*stateTrie.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ProcessSlotsStateGen")
defer span.End()
if state == nil {
return nil, errors.New("nil state")
}

if state.Slot() > slot {
err := fmt.Errorf("expected state.slot %d < slot %d", state.Slot(), slot)
return nil, err
}

if state.Slot() == slot {
return state, nil
}

for state.Slot() < slot {
state, err := ProcessSlot(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process slot")
}
if CanProcessEpoch(state) {
state, err = ProcessEpochPrecompute(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process epoch with optimizations")
}
}
state.SetSlot(state.Slot() + 1)
}

return state, nil
}
1 change: 1 addition & 0 deletions beacon-chain/stategen/BUILD.bazel
Expand Up @@ -14,6 +14,7 @@ go_library(
"//beacon-chain/db/filters:go_default_library",
"//beacon-chain/state:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/featureconfig:go_default_library",
"@com_github_prysmaticlabs_ethereumapis//eth/v1alpha1:go_default_library",
],
)
Expand Down
27 changes: 21 additions & 6 deletions beacon-chain/stategen/replay.go
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/featureconfig"
)

// ReplayBlocks replays the input blocks on the input state until the target slot is reached.
Expand All @@ -17,17 +18,31 @@ func (s *State) ReplayBlocks(ctx context.Context, state *state.BeaconState, sign
// The input block list is sorted in decreasing slots order.
if len(signed) > 0 {
for i := len(signed) - 1; i >= 0; i-- {
state, err = transition.ExecuteStateTransitionNoVerifyAttSigs(ctx, state, signed[i])
if err != nil {
return nil, err
if featureconfig.Get().EnableStateGenSigVerify {
state, err = transition.ExecuteStateTransition(ctx, state, signed[i])
if err != nil {
return nil, err
}
} else {
state, err = transition.ExecuteStateTransitionStateGen(ctx, state, signed[i])
if err != nil {
return nil, err
}
}
}
}

// If there is skip slots at the end.
state, err = transition.ProcessSlots(ctx, state, targetSlot)
if err != nil {
return nil, err
if featureconfig.Get().EnableStateGenSigVerify {
state, err = transition.ProcessSlots(ctx, state, targetSlot)
if err != nil {
return nil, err
}
} else {
state, err = transition.ProcessSlotsStateGen(ctx, state, targetSlot)
if err != nil {
return nil, err
}
}

return state, nil
Expand Down
6 changes: 5 additions & 1 deletion shared/featureconfig/config.go
Expand Up @@ -43,6 +43,7 @@ type Flags struct {
DisableUpdateHeadPerAttestation bool // DisableUpdateHeadPerAttestation will disabling update head on per attestation basis.
EnableByteMempool bool // EnaableByteMempool memory management.
EnableDomainDataCache bool // EnableDomainDataCache caches validator calls to DomainData per epoch.
EnableStateGenSigVerify bool // EnableStateGenSigVerify verifies proposer and randao signatures during state gen.

// DisableForkChoice disables using LMD-GHOST fork choice to update
// the head of the chain based on attestations and instead accepts any valid received block
Expand Down Expand Up @@ -144,7 +145,10 @@ func ConfigureBeaconChain(ctx *cli.Context) {
log.Warn("Enabling experimental memory management for beacon state")
cfg.EnableByteMempool = true
}

if ctx.GlobalBool(enableStateGenSigVerify.Name) {
log.Warn("Enabling sig verify for state gen")
cfg.EnableStateGenSigVerify = true
}
Init(cfg)
}

Expand Down
10 changes: 8 additions & 2 deletions shared/featureconfig/flags.go
Expand Up @@ -97,6 +97,11 @@ var (
Usage: "Enable caching of domain data requests per epoch. This feature reduces the total " +
"calls to the beacon node for each assignment.",
}
enableStateGenSigVerify = cli.BoolFlag{
Name: "enable-state-gen-sig-verify",
Usage: "Enable signature verification for state gen. This feature increases the cost to generate a historical state," +
"the resulting state is signature verified.",
}
)

// Deprecated flags list.
Expand Down Expand Up @@ -205,8 +210,8 @@ var (
Hidden: true,
}
deprecatedInitSyncCacheStateFlag = cli.BoolFlag{
Name: "initial-sync-cache-state",
Usage: deprecatedUsage,
Name: "initial-sync-cache-state",
Usage: deprecatedUsage,
Hidden: true,
}
)
Expand Down Expand Up @@ -268,6 +273,7 @@ var BeaconChainFlags = append(deprecatedFlags, []cli.Flag{
disableStrictAttestationPubsubVerificationFlag,
disableUpdateHeadPerAttestation,
enableByteMempool,
enableStateGenSigVerify,
Copy link
Member

Choose a reason for hiding this comment

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

Add to e2e flags?

Copy link
Member Author

Choose a reason for hiding this comment

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

Good call

}...)

// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.
Expand Down