2020-01-28 02:04:43 +00:00
|
|
|
package blockchain
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
|
2020-02-14 01:03:51 +00:00
|
|
|
stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state"
|
2020-08-24 22:11:45 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/attestationutil"
|
|
|
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
2020-01-28 02:04:43 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
2020-08-24 22:11:45 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/featureconfig"
|
2020-06-06 01:54:44 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/params"
|
2020-04-09 23:35:42 +00:00
|
|
|
"github.com/prysmaticlabs/prysm/shared/roughtime"
|
2020-01-28 02:04:43 +00:00
|
|
|
"go.opencensus.io/trace"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ErrTargetRootNotInDB returns when the target block root of an attestation cannot be found in the
|
|
|
|
// beacon database.
|
|
|
|
var ErrTargetRootNotInDB = errors.New("target root does not exist in db")
|
|
|
|
|
|
|
|
// onAttestation is called whenever an attestation is received, verifies the attestation is valid and saves
|
2020-06-24 03:14:44 +00:00
|
|
|
// it to the DB. As a stateless function, this does not hold nor delay attestation based on the spec descriptions.
|
|
|
|
// The delay is handled by the caller in `processAttestation`.
|
2020-01-28 02:04:43 +00:00
|
|
|
//
|
|
|
|
// Spec pseudocode definition:
|
2020-07-25 04:55:42 +00:00
|
|
|
// def on_attestation(store: Store, attestation: Attestation) -> None:
|
2020-01-28 02:04:43 +00:00
|
|
|
// """
|
|
|
|
// Run ``on_attestation`` upon receiving a new ``attestation`` from either within a block or directly on the wire.
|
|
|
|
//
|
|
|
|
// An ``attestation`` that is asserted as invalid may be valid at a later time,
|
|
|
|
// consider scheduling it for later processing in such case.
|
|
|
|
// """
|
2020-07-25 04:55:42 +00:00
|
|
|
// validate_on_attestation(store, attestation)
|
|
|
|
// store_target_checkpoint_state(store, attestation.data.target)
|
2020-01-28 02:04:43 +00:00
|
|
|
//
|
2020-07-25 04:55:42 +00:00
|
|
|
// # Get state at the `target` to fully validate attestation
|
|
|
|
// target_state = store.checkpoint_states[attestation.data.target]
|
2020-01-28 02:04:43 +00:00
|
|
|
// indexed_attestation = get_indexed_attestation(target_state, attestation)
|
|
|
|
// assert is_valid_indexed_attestation(target_state, indexed_attestation)
|
|
|
|
//
|
2020-07-25 04:55:42 +00:00
|
|
|
// # Update latest messages for attesting indices
|
|
|
|
// update_latest_messages(store, indexed_attestation.attesting_indices, attestation)
|
2020-06-09 22:40:48 +00:00
|
|
|
// TODO(#6072): This code path is highly untested. Requires comprehensive tests and simpler refactoring.
|
2020-01-28 02:04:43 +00:00
|
|
|
func (s *Service) onAttestation(ctx context.Context, a *ethpb.Attestation) ([]uint64, error) {
|
2020-07-09 23:50:48 +00:00
|
|
|
ctx, span := trace.StartSpan(ctx, "blockChain.onAttestation")
|
2020-01-28 02:04:43 +00:00
|
|
|
defer span.End()
|
|
|
|
|
2020-04-16 15:39:53 +00:00
|
|
|
if a == nil {
|
|
|
|
return nil, errors.New("nil attestation")
|
|
|
|
}
|
|
|
|
if a.Data == nil {
|
|
|
|
return nil, errors.New("nil attestation.Data field")
|
|
|
|
}
|
|
|
|
if a.Data.Target == nil {
|
|
|
|
return nil, errors.New("nil attestation.Data.Target field")
|
|
|
|
}
|
|
|
|
|
2020-02-14 01:03:51 +00:00
|
|
|
tgt := stateTrie.CopyCheckpoint(a.Data.Target)
|
2020-01-28 02:04:43 +00:00
|
|
|
|
|
|
|
if helpers.SlotToEpoch(a.Data.Slot) != a.Data.Target.Epoch {
|
|
|
|
return nil, fmt.Errorf("data slot is not in the same epoch as target %d != %d", helpers.SlotToEpoch(a.Data.Slot), a.Data.Target.Epoch)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify beacon node has seen the target block before.
|
2020-02-10 21:48:28 +00:00
|
|
|
if !s.hasBlock(ctx, bytesutil.ToBytes32(tgt.Root)) {
|
2020-01-28 02:04:43 +00:00
|
|
|
return nil, ErrTargetRootNotInDB
|
|
|
|
}
|
|
|
|
|
2020-08-24 22:11:45 +00:00
|
|
|
if featureconfig.Get().UseCheckPointInfoCache {
|
|
|
|
c, err := s.AttestationCheckPtInfo(ctx, a)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := s.verifyAttTargetEpoch(ctx, params.BeaconConfig().GenesisTime, uint64(roughtime.Now().Unix()), tgt); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if err := s.verifyBeaconBlock(ctx, a.Data); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not verify attestation beacon block")
|
|
|
|
}
|
|
|
|
if err := s.verifyLMDFFGConsistent(ctx, a.Data.Target.Epoch, a.Data.Target.Root, a.Data.BeaconBlockRoot); err != nil {
|
|
|
|
return nil, errors.Wrap(err, "could not verify attestation beacon block")
|
|
|
|
}
|
|
|
|
if err := helpers.VerifySlotTime(params.BeaconConfig().GenesisTime, a.Data.Slot+1, params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
committee, err := helpers.BeaconCommittee(c.ActiveIndices, bytesutil.ToBytes32(c.Seed), a.Data.Slot, a.Data.CommitteeIndex)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
indexedAtt := attestationutil.ConvertToIndexed(ctx, a, committee)
|
|
|
|
if err := attestationutil.IsValidAttestationIndices(ctx, indexedAtt); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
domain, err := helpers.Domain(c.Fork, indexedAtt.Data.Target.Epoch, params.BeaconConfig().DomainBeaconAttester, c.GenesisRoot)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
indices := indexedAtt.AttestingIndices
|
|
|
|
pubkeys := []bls.PublicKey{}
|
|
|
|
for i := 0; i < len(indices); i++ {
|
|
|
|
pubkeyAtIdx := c.PubKeys[indices[i]]
|
|
|
|
pk, err := bls.PublicKeyFromBytes(pubkeyAtIdx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
pubkeys = append(pubkeys, pk)
|
|
|
|
}
|
|
|
|
if err := attestationutil.VerifyIndexedAttestationSig(ctx, indexedAtt, pubkeys, domain); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
s.forkChoiceStore.ProcessAttestation(ctx, indexedAtt.AttestingIndices, bytesutil.ToBytes32(a.Data.BeaconBlockRoot), a.Data.Target.Epoch)
|
|
|
|
|
|
|
|
return indexedAtt.AttestingIndices, nil
|
|
|
|
}
|
|
|
|
|
2020-01-30 19:06:20 +00:00
|
|
|
// Retrieve attestation's data beacon block pre state. Advance pre state to latest epoch if necessary and
|
|
|
|
// save it to the cache.
|
|
|
|
baseState, err := s.getAttPreState(ctx, tgt)
|
2020-01-28 02:04:43 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-11 19:36:32 +00:00
|
|
|
genesisTime := helpers.GenesisTime(baseState)
|
2020-01-31 20:57:01 +00:00
|
|
|
|
2020-01-28 02:04:43 +00:00
|
|
|
// Verify attestation target is from current epoch or previous epoch.
|
2020-04-09 23:35:42 +00:00
|
|
|
if err := s.verifyAttTargetEpoch(ctx, genesisTime, uint64(roughtime.Now().Unix()), tgt); err != nil {
|
2020-01-28 02:04:43 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify attestation beacon block is known and not from the future.
|
|
|
|
if err := s.verifyBeaconBlock(ctx, a.Data); err != nil {
|
2020-06-09 22:40:48 +00:00
|
|
|
return nil, errors.Wrap(err, "could not verify attestation beacon block")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify LMG GHOST and FFG votes are consistent with each other.
|
|
|
|
if err := s.verifyLMDFFGConsistent(ctx, a.Data.Target.Epoch, a.Data.Target.Root, a.Data.BeaconBlockRoot); err != nil {
|
2020-01-28 02:04:43 +00:00
|
|
|
return nil, errors.Wrap(err, "could not verify attestation beacon block")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify attestations can only affect the fork choice of subsequent slots.
|
2020-06-06 01:54:44 +00:00
|
|
|
if err := helpers.VerifySlotTime(genesisTime, a.Data.Slot+1, params.BeaconNetworkConfig().MaximumGossipClockDisparity); err != nil {
|
2020-01-28 02:04:43 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-06-23 21:11:20 +00:00
|
|
|
// Use the target state to validate attestation and calculate the committees.
|
2020-01-28 02:04:43 +00:00
|
|
|
indexedAtt, err := s.verifyAttestation(ctx, baseState, a)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-04-14 20:27:03 +00:00
|
|
|
if indexedAtt.AttestingIndices == nil {
|
|
|
|
return nil, errors.New("nil attesting indices")
|
|
|
|
}
|
|
|
|
|
2020-02-08 02:05:43 +00:00
|
|
|
// Update forkchoice store with the new attestation for updating weight.
|
|
|
|
s.forkChoiceStore.ProcessAttestation(ctx, indexedAtt.AttestingIndices, bytesutil.ToBytes32(a.Data.BeaconBlockRoot), a.Data.Target.Epoch)
|
|
|
|
|
2020-01-28 02:04:43 +00:00
|
|
|
return indexedAtt.AttestingIndices, nil
|
|
|
|
}
|