prysm-pulse/beacon-chain/core/state/transition.go
terence tsao 798bbbdc82 Cold start for interop (#3437)
* coldstart flags for validator

* WIP beacon node flags

* wip beacon chain, flag fix in validator, arg fix in validator

* checkpoint

* Added interop service

* working on mock chainstart

* save the state lol

* fix tests

* Save genesis validators

* gaz

* fix validator help flags

* WaitForChainStart actually waits for genesis time

* cold start fixes

* cache

* change back

* allow for genesis state too

* remove logs

* increase mmap size

* dont process if head doesn't exist

* add 10ms tolerance

* enable libp2p debug at debug, fix pubsub

* works with checkpt

* initialize justified and finalized in db

* Removed preloadStatePath from blockchain service

* Clean up

* Write to disk for now post state

* revert b466dd536f8eadbdae2264a545a755370223d917

* Builds

* Only RPC test fails now

* use minimal config, no demo config

* clean up branch

* Lint

* resolve lint

* more lint fixes

* lint

* fix viz

* Fixing RPC test

* skip before epoch 2

* RPC time out

* Fixed ordering

* rename

* remove some dbg statements

* ensure index is correct

* fix some panics

* getting closer

* fix tests

* Fix private key

* Fixed RPC test

* Fixed beacon chain build for docker

* Add interop.go to validator go_image

* Fixed docker build

* handle errors

* skip test, skip disconnecting peers

* Fixed docker build

* tolerance for attestation processing

* revert copy

* clearer err message parse

* fix launching from dep contract
2019-09-11 13:38:35 -05:00

577 lines
20 KiB
Go

// Package state implements the whole state transition
// function which consists of per slot, per-epoch transitions.
// It also bootstraps the genesis beacon state for slot 0.
package state
import (
"bytes"
"context"
"fmt"
"github.com/gogo/protobuf/proto"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-ssz"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
e "github.com/prysmaticlabs/prysm/beacon-chain/core/epoch"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/interop"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/mathutil"
"github.com/prysmaticlabs/prysm/shared/params"
"go.opencensus.io/trace"
)
// ExecuteStateTransition defines the procedure for a state transition function.
//
// Spec pseudocode definition:
// def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState:
// # Process slots (including those with no blocks) since block
// process_slots(state, block.slot)
// # Process block
// process_block(state, block)
// # Validate state root (`validate_state_root == True` in production)
// if validate_state_root:
// assert block.state_root == hash_tree_root(state)
// # Return post-state
// return state
func ExecuteStateTransition(
ctx context.Context,
state *pb.BeaconState,
block *ethpb.BeaconBlock,
) (*pb.BeaconState, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
helpers.ClearStartShardCache()
b.ClearEth1DataVoteCache()
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ExecuteStateTransition")
defer span.End()
var err error
// Execute per slots transition.
state, err = ProcessSlots(ctx, state, block.Slot)
if err != nil {
return nil, errors.Wrap(err, "could not process slot")
}
// Execute per block transition.
if block != nil {
state, err = ProcessBlock(ctx, state, block)
if err != nil {
return nil, errors.Wrap(err, "could not process block")
}
}
interop.WriteStateToDisk(state)
postStateRoot, err := ssz.HashTreeRoot(state)
if err != nil {
return nil, errors.Wrap(err, "could not tree hash processed state")
}
if !bytes.Equal(postStateRoot[:], block.StateRoot) {
return nil, fmt.Errorf("validate state root failed, wanted: %#x, received: %#x",
postStateRoot[:], block.StateRoot)
}
return state, nil
}
// ExecuteStateTransitionNoVerify defines the procedure for a state transition function.
// This does not validate state root, The use case of such is for state root calculation, the proposer
// should first run state transition on an unsigned block containing a stub for the state root and signature.
// This does not modify state.
//
// WARNING: This method does not validate state root and proposer signature. This is used for proposer to compute
// state root before proposing a new block, and this does not modify state.
//
// Spec pseudocode definition:
// def state_transition(state: BeaconState, block: BeaconBlock, validate_state_root: bool=False) -> BeaconState:
// # Process slots (including those with no blocks) since block
// process_slots(state, block.slot)
// # Process block
// process_block(state, block)
// # Return post-state
// return state
func ExecuteStateTransitionNoVerify(
ctx context.Context,
state *pb.BeaconState,
block *ethpb.BeaconBlock,
) (*pb.BeaconState, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
stateCopy := proto.Clone(state).(*pb.BeaconState)
helpers.ClearStartShardCache()
b.ClearEth1DataVoteCache()
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.ExecuteStateTransition")
defer span.End()
var err error
// Execute per slots transition.
stateCopy, err = ProcessSlots(ctx, stateCopy, block.Slot)
if err != nil {
return nil, errors.Wrap(err, "could not process slot")
}
// Execute per block transition.
if block != nil {
stateCopy, err = processBlockNoVerify(ctx, stateCopy, block)
if err != nil {
return nil, errors.Wrap(err, "could not process block")
}
}
return stateCopy, nil
}
// ProcessSlot happens every slot and focuses on the slot counter and block roots record updates.
// It happens regardless if there's an incoming block or not.
// Spec pseudocode definition:
//
// def process_slot(state: BeaconState) -> None:
// # Cache state root
// previous_state_root = hash_tree_root(state)
// state.state_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_state_root
//
// # Cache latest block header state root
// if state.latest_block_header.state_root == Bytes32():
// state.latest_block_header.state_root = previous_state_root
//
// # Cache block root
// previous_block_root = signing_root(state.latest_block_header)
// state.block_roots[state.slot % SLOTS_PER_HISTORICAL_ROOT] = previous_block_root
func ProcessSlot(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessSlot")
defer span.End()
span.AddAttributes(trace.Int64Attribute("slot", int64(state.Slot)))
prevStateRoot, err := ssz.HashTreeRoot(state)
if err != nil {
return nil, errors.Wrap(err, "could not tree hash prev state root")
}
state.StateRoots[state.Slot%params.BeaconConfig().SlotsPerHistoricalRoot] = prevStateRoot[:]
zeroHash := params.BeaconConfig().ZeroHash
// Cache latest block header state root.
if bytes.Equal(state.LatestBlockHeader.StateRoot, zeroHash[:]) {
state.LatestBlockHeader.StateRoot = prevStateRoot[:]
}
prevBlockRoot, err := ssz.SigningRoot(state.LatestBlockHeader)
if err != nil {
return nil, errors.Wrap(err, "could not determine prev block root")
}
// Cache the block root.
state.BlockRoots[state.Slot%params.BeaconConfig().SlotsPerHistoricalRoot] = prevBlockRoot[:]
return state, nil
}
// ProcessSlots process through skip skips and apply epoch transition when it's needed
//
// Spec pseudocode definition:
// def process_slots(state: BeaconState, slot: Slot) -> None:
// assert state.slot <= slot
// while state.slot < slot:
// process_slot(state)
// # Process epoch on the first slot of the next epoch
// if (state.slot + 1) % SLOTS_PER_EPOCH == 0:
// process_epoch(state)
// state.slot += 1
// ]
func ProcessSlots(ctx context.Context, state *pb.BeaconState, slot uint64) (*pb.BeaconState, error) {
if state.Slot > slot {
return nil, fmt.Errorf("expected state.slot %d < slot %d", state.Slot, slot)
}
for state.Slot < slot {
if ctx.Err() != nil {
return nil, ctx.Err()
}
state, err := ProcessSlot(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process slot")
}
if CanProcessEpoch(state) {
state, err = ProcessEpoch(ctx, state)
if err != nil {
return nil, errors.Wrap(err, "could not process epoch")
}
}
state.Slot++
}
return state, nil
}
// ProcessBlock creates a new, modified beacon state by applying block operation
// transformations as defined in the Ethereum Serenity specification, including processing proposer slashings,
// processing block attestations, and more.
//
// Spec pseudocode definition:
//
// def process_block(state: BeaconState, block: BeaconBlock) -> None:
// process_block_header(state, block)
// process_randao(state, block.body)
// process_eth1_data(state, block.body)
// process_operations(state, block.body)
func ProcessBlock(
ctx context.Context,
state *pb.BeaconState,
block *ethpb.BeaconBlock,
) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessBlock")
defer span.End()
state, err := b.ProcessBlockHeader(state, block)
if err != nil {
return nil, errors.Wrap(err, "could not process block header")
}
state, err = b.ProcessRandao(state, block.Body)
if err != nil {
return nil, errors.Wrap(err, "could not verify and process randao")
}
state, err = b.ProcessEth1DataInBlock(state, block)
if err != nil {
return nil, errors.Wrap(err, "could not process eth1 data")
}
state, err = ProcessOperations(ctx, state, block.Body)
if err != nil {
return nil, errors.Wrap(err, "could not process block operation")
}
return state, nil
}
// processBlockNoVerify creates a new, modified beacon state by applying block operation
// transformations as defined in the Ethereum Serenity specification. It does not validate
// block signature.
//
//
// WARNING: This method does not verify proposer signature. This is used for proposer to compute state root
// using a unsigned block.
//
// Spec pseudocode definition:
//
// def process_block(state: BeaconState, block: BeaconBlock) -> None:
// process_block_header(state, block)
// process_randao(state, block.body)
// process_eth1_data(state, block.body)
// process_operations(state, block.body)
func processBlockNoVerify(
ctx context.Context,
state *pb.BeaconState,
block *ethpb.BeaconBlock,
) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessBlock")
defer span.End()
state, err := b.ProcessBlockHeaderNoVerify(state, block)
if err != nil {
return nil, errors.Wrap(err, "could not process block header")
}
state, err = b.ProcessRandao(state, block.Body)
if err != nil {
return nil, errors.Wrap(err, "could not verify and process randao")
}
state, err = b.ProcessEth1DataInBlock(state, block)
if err != nil {
return nil, errors.Wrap(err, "could not process eth1 data")
}
state, err = processOperationsNoVerify(ctx, state, block.Body)
if err != nil {
return nil, errors.Wrap(err, "could not process block operation")
}
return state, nil
}
// ProcessOperations processes the operations in the beacon block and updates beacon state
// with the operations in block.
//
// Spec pseudocode definition:
//
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
// # Verify that outstanding deposits are processed up to the maximum number of deposits
// assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
// # Verify that there are no duplicate transfers
// assert len(body.transfers) == len(set(body.transfers))
//
// all_operations = (
// (body.proposer_slashings, process_proposer_slashing),
// (body.attester_slashings, process_attester_slashing),
// (body.attestations, process_attestation),
// (body.deposits, process_deposit),
// (body.voluntary_exits, process_voluntary_exit),
// (body.transfers, process_transfer),
// ) # type: Sequence[Tuple[List, Callable]]
// for operations, function in all_operations:
// for operation in operations:
// function(state, operation)
func ProcessOperations(
ctx context.Context,
state *pb.BeaconState,
body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessOperations")
defer span.End()
if err := verifyOperationLengths(state, body); err != nil {
return nil, errors.Wrap(err, "could not verify operation lengths")
}
// Verify that there are no duplicate transfers
transferSet := make(map[[32]byte]bool)
for _, transfer := range body.Transfers {
h, err := hashutil.HashProto(transfer)
if err != nil {
return nil, errors.Wrap(err, "could not hash transfer")
}
if transferSet[h] {
return nil, fmt.Errorf("duplicate transfer: %v", transfer)
}
transferSet[h] = true
}
state, err := b.ProcessProposerSlashings(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block proposer slashings")
}
state, err = b.ProcessAttesterSlashings(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block attester slashings")
}
state, err = b.ProcessAttestations(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block attestations")
}
state, err = b.ProcessDeposits(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block validator deposits")
}
state, err = b.ProcessVoluntaryExits(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process validator exits")
}
state, err = b.ProcessTransfers(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block transfers")
}
return state, nil
}
// processOperationsNoVerify processes the operations in the beacon block and updates beacon state
// with the operations in block. It does not validate the attestation signature
//
// WARNING: This method does not verify attestation signature. This is used for proposer to compute state root
// using a unsigned block.
//
// Spec pseudocode definition:
//
// def process_operations(state: BeaconState, body: BeaconBlockBody) -> None:
// # Verify that outstanding deposits are processed up to the maximum number of deposits
// assert len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)
// # Verify that there are no duplicate transfers
// assert len(body.transfers) == len(set(body.transfers))
//
// all_operations = (
// (body.proposer_slashings, process_proposer_slashing),
// (body.attester_slashings, process_attester_slashing),
// (body.attestations, process_attestation),
// (body.deposits, process_deposit),
// (body.voluntary_exits, process_voluntary_exit),
// (body.transfers, process_transfer),
// ) # type: Sequence[Tuple[List, Callable]]
// for operations, function in all_operations:
// for operation in operations:
// function(state, operation)
func processOperationsNoVerify(
ctx context.Context,
state *pb.BeaconState,
body *ethpb.BeaconBlockBody) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessOperations")
defer span.End()
if err := verifyOperationLengths(state, body); err != nil {
return nil, errors.Wrap(err, "could not verify operation lengths")
}
// Verify that there are no duplicate transfers
transferSet := make(map[[32]byte]bool)
for _, transfer := range body.Transfers {
h, err := hashutil.HashProto(transfer)
if err != nil {
return nil, errors.Wrap(err, "could not hash transfer")
}
if transferSet[h] {
return nil, fmt.Errorf("duplicate transfer: %v", transfer)
}
transferSet[h] = true
}
state, err := b.ProcessProposerSlashings(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block proposer slashings")
}
state, err = b.ProcessAttesterSlashings(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block attester slashings")
}
state, err = b.ProcessAttestationsNoVerify(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block attestations")
}
state, err = b.ProcessDeposits(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block validator deposits")
}
state, err = b.ProcessVoluntaryExits(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process validator exits")
}
state, err = b.ProcessTransfers(state, body)
if err != nil {
return nil, errors.Wrap(err, "could not process block transfers")
}
return state, nil
}
func verifyOperationLengths(state *pb.BeaconState, body *ethpb.BeaconBlockBody) error {
if uint64(len(body.ProposerSlashings)) > params.BeaconConfig().MaxProposerSlashings {
return fmt.Errorf(
"number of proposer slashings (%d) in block body exceeds allowed threshold of %d",
len(body.ProposerSlashings),
params.BeaconConfig().MaxProposerSlashings,
)
}
if uint64(len(body.AttesterSlashings)) > params.BeaconConfig().MaxAttesterSlashings {
return fmt.Errorf(
"number of attester slashings (%d) in block body exceeds allowed threshold of %d",
len(body.AttesterSlashings),
params.BeaconConfig().MaxAttesterSlashings,
)
}
if uint64(len(body.Attestations)) > params.BeaconConfig().MaxAttestations {
return fmt.Errorf(
"number of attestations (%d) in block body exceeds allowed threshold of %d",
len(body.Attestations),
params.BeaconConfig().MaxAttestations,
)
}
if uint64(len(body.VoluntaryExits)) > params.BeaconConfig().MaxVoluntaryExits {
return fmt.Errorf(
"number of voluntary exits (%d) in block body exceeds allowed threshold of %d",
len(body.VoluntaryExits),
params.BeaconConfig().MaxVoluntaryExits,
)
}
if uint64(len(body.Transfers)) > params.BeaconConfig().MaxTransfers {
return fmt.Errorf(
"number of transfers (%d) in block body exceeds allowed threshold of %d",
len(body.Transfers),
params.BeaconConfig().MaxTransfers,
)
}
if state.Eth1DepositIndex > state.Eth1Data.DepositCount {
return fmt.Errorf("expected state.deposit_index %d <= eth1data.deposit_count %d", state.Eth1DepositIndex, state.Eth1Data.DepositCount)
}
maxDeposits := mathutil.Min(params.BeaconConfig().MaxDeposits, state.Eth1Data.DepositCount-state.Eth1DepositIndex)
// Verify outstanding deposits are processed up to max number of deposits
if len(body.Deposits) != int(maxDeposits) {
return fmt.Errorf("incorrect outstanding deposits in block body, wanted: %d, got: %d",
maxDeposits, len(body.Deposits))
}
return nil
}
// CanProcessEpoch checks the eligibility to process epoch.
// The epoch can be processed at the end of the last slot of every epoch
//
// Spec pseudocode definition:
// If (state.slot + 1) % SLOTS_PER_EPOCH == 0:
func CanProcessEpoch(state *pb.BeaconState) bool {
return (state.Slot+1)%params.BeaconConfig().SlotsPerEpoch == 0
}
// ProcessEpoch describes the per epoch operations that are performed on the
// beacon state. It focuses on the validator registry, adjusting balances, and finalizing slots.
//
// Spec pseudocode definition:
//
// def process_epoch(state: BeaconState) -> None:
// process_justification_and_finalization(state)
// process_crosslinks(state)
// process_rewards_and_penalties(state)
// process_registry_updates(state)
// # @process_reveal_deadlines
// # @process_challenge_deadlines
// process_slashings(state)
// process_final_updates(state)
// # @after_process_final_updates
func ProcessEpoch(ctx context.Context, state *pb.BeaconState) (*pb.BeaconState, error) {
ctx, span := trace.StartSpan(ctx, "beacon-chain.ChainService.state.ProcessEpoch")
defer span.End()
span.AddAttributes(trace.Int64Attribute("epoch", int64(helpers.SlotToEpoch(state.Slot))))
prevEpochAtts, err := e.MatchAttestations(state, helpers.PrevEpoch(state))
if err != nil {
return nil, fmt.Errorf("could not get target atts prev epoch %d: %v",
helpers.PrevEpoch(state), err)
}
currentEpochAtts, err := e.MatchAttestations(state, helpers.CurrentEpoch(state))
if err != nil {
return nil, fmt.Errorf("could not get target atts current epoch %d: %v",
helpers.CurrentEpoch(state), err)
}
prevEpochAttestedBalance, err := e.AttestingBalance(state, prevEpochAtts.Target)
if err != nil {
return nil, errors.Wrap(err, "could not get attesting balance prev epoch")
}
currentEpochAttestedBalance, err := e.AttestingBalance(state, currentEpochAtts.Target)
if err != nil {
return nil, errors.Wrap(err, "could not get attesting balance current epoch")
}
state, err = e.ProcessJustificationAndFinalization(state, prevEpochAttestedBalance, currentEpochAttestedBalance)
if err != nil {
return nil, errors.Wrap(err, "could not process justification")
}
state, err = e.ProcessCrosslinks(state)
if err != nil {
return nil, errors.Wrap(err, "could not process crosslink")
}
state, err = e.ProcessRewardsAndPenalties(state)
if err != nil {
return nil, errors.Wrap(err, "could not process rewards and penalties")
}
state, err = e.ProcessRegistryUpdates(state)
if err != nil {
return nil, errors.Wrap(err, "could not process registry updates")
}
state, err = e.ProcessSlashings(state)
if err != nil {
return nil, errors.Wrap(err, "could not process slashings")
}
state, err = e.ProcessFinalUpdates(state)
if err != nil {
return nil, errors.Wrap(err, "could not process final updates")
}
return state, nil
}