2020-01-28 02:04:43 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
2020-03-01 15:22:49 +00:00
|
|
|
"bytes"
|
2020-01-28 02:04:43 +00:00
|
|
|
"context"
|
2020-02-18 18:05:36 +00:00
|
|
|
"encoding/hex"
|
2020-01-28 02:04:43 +00:00
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
|
2020-01-31 20:57:01 +00:00
|
|
|
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
2020-01-29 01:44:51 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
2020-01-28 02:04:43 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2020-03-02 06:06:21 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
2020-01-28 02:04:43 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
|
|
|
)
|
|
|
|
|
2020-01-30 19:06:20 +00:00
|
|
|
// getAttPreState retrieves the att pre state by either from the cache or the DB.
|
2020-01-31 20:57:01 +00:00
|
|
|
func (s *Service) getAttPreState(ctx context.Context, c *ethpb.Checkpoint) (*stateTrie.BeaconState, error) {
|
2020-01-30 19:06:20 +00:00
|
|
|
s.checkpointStateLock.Lock()
|
|
|
|
defer s.checkpointStateLock.Unlock()
|
|
|
|
cachedState, err := s.checkpointState.StateByCheckpoint(c)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not get cached checkpoint state")
|
|
|
|
}
|
|
|
|
if cachedState != nil {
|
|
|
|
return cachedState, nil
|
|
|
|
}
|
2020-03-16 19:07:07 +00:00
|
|
|
|
2020-04-21 22:30:22 +00:00
|
|
|
if featureconfig.Get().NewStateMgmt {
|
2020-04-18 23:29:58 +00:00
|
|
|
if !s.stateGen.HasState(ctx, bytesutil.ToBytes32(c.Root)) {
|
|
|
|
if err := s.beaconDB.SaveBlocks(ctx, s.getInitSyncBlocks()); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not save initial sync blocks")
|
|
|
|
}
|
|
|
|
s.clearInitSyncBlocks()
|
|
|
|
}
|
2020-04-23 19:03:51 +00:00
|
|
|
|
|
|
|
baseState, err := s.stateGen.StateByRoot(ctx, bytesutil.ToBytes32(c.Root))
|
2020-03-01 15:22:49 +00:00
|
|
|
if err != nil {
|
2020-03-16 19:07:07 +00:00
|
|
|
return nil, errors.Wrapf(err, "could not get pre state for slot %d", helpers.StartSlot(c.Epoch))
|
2020-03-01 15:22:49 +00:00
|
|
|
}
|
2020-04-23 19:03:51 +00:00
|
|
|
|
|
|
|
if helpers.StartSlot(c.Epoch) > baseState.Slot() {
|
2020-06-14 06:34:33 +00:00
|
|
|
baseState = baseState.Copy()
|
2020-04-23 19:03:51 +00:00
|
|
|
baseState, err = state.ProcessSlots(ctx, baseState, helpers.StartSlot(c.Epoch))
|
2020-03-02 06:06:21 +00:00
|
|
|
if err != nil {
|
2020-04-23 19:03:51 +00:00
|
|
|
return nil, errors.Wrapf(err, "could not process slots up to %d", helpers.StartSlot(c.Epoch))
|
2020-03-02 06:06:21 +00:00
|
|
|
}
|
2020-03-01 15:22:49 +00:00
|
|
|
}
|
2020-01-30 19:06:20 +00:00
|
|
|
|
2020-04-23 19:03:51 +00:00
|
|
|
if err := s.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
|
|
|
Checkpoint: c,
|
|
|
|
State: baseState,
|
|
|
|
}); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not saved checkpoint state to cache")
|
|
|
|
}
|
|
|
|
|
|
|
|
return baseState, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if featureconfig.Get().CheckHeadState {
|
|
|
|
headRoot, err := s.HeadRoot(ctx)
|
2020-03-16 19:07:07 +00:00
|
|
|
if err != nil {
|
2020-04-23 19:03:51 +00:00
|
|
|
return nil, errors.Wrapf(err, "could not get head root")
|
|
|
|
}
|
|
|
|
if bytes.Equal(headRoot, c.Root) {
|
|
|
|
st, err := s.HeadState(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not get head state")
|
|
|
|
}
|
|
|
|
if err := s.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
|
|
|
Checkpoint: c,
|
|
|
|
State: st.Copy(),
|
|
|
|
}); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not saved checkpoint state to cache")
|
|
|
|
}
|
|
|
|
return st, nil
|
2020-03-16 19:07:07 +00:00
|
|
|
}
|
2020-01-28 02:04:43 +00:00
|
|
|
}
|
2020-03-16 19:07:07 +00:00
|
|
|
|
2020-04-23 19:03:51 +00:00
|
|
|
baseState, err := s.beaconDB.State(ctx, bytesutil.ToBytes32(c.Root))
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not get pre state for slot %d", helpers.StartSlot(c.Epoch))
|
|
|
|
}
|
2020-01-28 02:04:43 +00:00
|
|
|
if baseState == nil {
|
|
|
|
return nil, fmt.Errorf("pre state of target block %d does not exist", helpers.StartSlot(c.Epoch))
|
|
|
|
}
|
2020-01-30 19:06:20 +00:00
|
|
|
|
2020-02-02 05:23:05 +00:00
|
|
|
if helpers.StartSlot(c.Epoch) > baseState.Slot() {
|
2020-06-14 06:34:33 +00:00
|
|
|
savedState := baseState.Copy()
|
|
|
|
savedState, err = state.ProcessSlots(ctx, savedState, helpers.StartSlot(c.Epoch))
|
2020-01-30 19:06:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrapf(err, "could not process slots up to %d", helpers.StartSlot(c.Epoch))
|
|
|
|
}
|
2020-04-23 19:03:51 +00:00
|
|
|
if err := s.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
|
|
|
Checkpoint: c,
|
2020-06-14 06:34:33 +00:00
|
|
|
State: savedState.Copy(),
|
2020-04-23 19:03:51 +00:00
|
|
|
}); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not saved checkpoint state to cache")
|
|
|
|
}
|
2020-06-14 06:34:33 +00:00
|
|
|
return savedState, nil
|
2020-01-30 19:06:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.checkpointState.AddCheckpointState(&cache.CheckpointState{
|
|
|
|
Checkpoint: c,
|
2020-06-14 06:34:33 +00:00
|
|
|
State: baseState.Copy(),
|
2020-01-30 19:06:20 +00:00
|
|
|
}); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not saved checkpoint state to cache")
|
|
|
|
}
|
|
|
|
|
2020-02-02 05:23:05 +00:00
|
|
|
return baseState, nil
|
2020-01-28 02:04:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// verifyAttTargetEpoch validates attestation is from the current or previous epoch.
|
|
|
|
func (s *Service) verifyAttTargetEpoch(ctx context.Context, genesisTime uint64, nowTime uint64, c *ethpb.Checkpoint) error {
|
|
|
|
currentSlot := (nowTime - genesisTime) / params.BeaconConfig().SecondsPerSlot
|
|
|
|
currentEpoch := helpers.SlotToEpoch(currentSlot)
|
|
|
|
var prevEpoch uint64
|
|
|
|
// Prevents previous epoch under flow
|
|
|
|
if currentEpoch > 1 {
|
|
|
|
prevEpoch = currentEpoch - 1
|
|
|
|
}
|
|
|
|
if c.Epoch != prevEpoch && c.Epoch != currentEpoch {
|
|
|
|
return fmt.Errorf("target epoch %d does not match current epoch %d or prev epoch %d", c.Epoch, currentEpoch, prevEpoch)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// verifyBeaconBlock verifies beacon head block is known and not from the future.
|
|
|
|
func (s *Service) verifyBeaconBlock(ctx context.Context, data *ethpb.AttestationData) error {
|
|
|
|
b, err := s.beaconDB.Block(ctx, bytesutil.ToBytes32(data.BeaconBlockRoot))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if b == nil || b.Block == nil {
|
|
|
|
return fmt.Errorf("beacon block %#x does not exist", bytesutil.Trunc(data.BeaconBlockRoot))
|
|
|
|
}
|
|
|
|
if b.Block.Slot > data.Slot {
|
2020-03-24 04:30:28 +00:00
|
|
|
return fmt.Errorf("could not process attestation for future block, block.Slot=%d > attestation.Data.Slot=%d", b.Block.Slot, data.Slot)
|
2020-01-28 02:04:43 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-06-09 22:40:48 +00:00
|
|
|
// verifyLMDFFGConsistent verifies LMD GHOST and FFG votes are consistent with each other.
|
|
|
|
func (s *Service) verifyLMDFFGConsistent(ctx context.Context, ffgEpoch uint64, ffgRoot []byte, lmdRoot []byte) error {
|
|
|
|
ffgSlot := helpers.StartSlot(ffgEpoch)
|
|
|
|
r, err := s.ancestor(ctx, lmdRoot, ffgSlot)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !bytes.Equal(ffgRoot, r) {
|
|
|
|
return errors.New("FFG and LMD votes are not consistent")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-28 02:04:43 +00:00
|
|
|
// verifyAttestation validates input attestation is valid.
|
2020-01-31 20:57:01 +00:00
|
|
|
func (s *Service) verifyAttestation(ctx context.Context, baseState *stateTrie.BeaconState, a *ethpb.Attestation) (*ethpb.IndexedAttestation, error) {
|
2020-01-28 02:04:43 +00:00
|
|
|
committee, err := helpers.BeaconCommitteeFromState(baseState, a.Data.Slot, a.Data.CommitteeIndex)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-22 00:23:37 +00:00
|
|
|
indexedAtt := attestationutil.ConvertToIndexed(ctx, a, committee)
|
2020-01-28 02:04:43 +00:00
|
|
|
if err := blocks.VerifyIndexedAttestation(ctx, baseState, indexedAtt); err != nil {
|
2020-04-14 20:27:03 +00:00
|
|
|
if err == helpers.ErrSigFailedToVerify {
|
2020-02-18 18:05:36 +00:00
|
|
|
// When sig fails to verify, check if there's a differences in committees due to
|
|
|
|
// different seeds.
|
2020-03-16 19:07:07 +00:00
|
|
|
var aState *stateTrie.BeaconState
|
|
|
|
var err error
|
2020-04-21 22:30:22 +00:00
|
|
|
if featureconfig.Get().NewStateMgmt {
|
2020-04-18 23:29:58 +00:00
|
|
|
if !s.stateGen.HasState(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot)) {
|
|
|
|
if err := s.beaconDB.SaveBlocks(ctx, s.getInitSyncBlocks()); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not save initial sync blocks")
|
|
|
|
}
|
|
|
|
s.clearInitSyncBlocks()
|
|
|
|
}
|
2020-03-16 19:07:07 +00:00
|
|
|
aState, err = s.stateGen.StateByRoot(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot))
|
2020-04-14 20:27:03 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
aState, err = s.beaconDB.State(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-03-16 19:07:07 +00:00
|
|
|
}
|
2020-04-14 20:27:03 +00:00
|
|
|
if aState == nil {
|
|
|
|
return nil, fmt.Errorf("nil state for block root %#x", a.Data.BeaconBlockRoot)
|
2020-02-18 18:05:36 +00:00
|
|
|
}
|
|
|
|
epoch := helpers.SlotToEpoch(a.Data.Slot)
|
|
|
|
origSeed, err := helpers.Seed(baseState, epoch, params.BeaconConfig().DomainBeaconAttester)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not get original seed")
|
|
|
|
}
|
|
|
|
|
|
|
|
aSeed, err := helpers.Seed(aState, epoch, params.BeaconConfig().DomainBeaconAttester)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not get attester's seed")
|
|
|
|
}
|
|
|
|
if origSeed != aSeed {
|
|
|
|
return nil, fmt.Errorf("could not verify indexed attestation due to differences in seeds: %v != %v",
|
|
|
|
hex.EncodeToString(bytesutil.Trunc(origSeed[:])), hex.EncodeToString(bytesutil.Trunc(aSeed[:])))
|
|
|
|
}
|
|
|
|
}
|
2020-01-28 02:04:43 +00:00
|
|
|
return nil, errors.Wrap(err, "could not verify indexed attestation")
|
|
|
|
}
|
|
|
|
|
|
|
|
return indexedAtt, nil
|
|
|
|
}
|