Implement ProduceAttestationData in the validator API (#9271)

* functionality

* test

* build file

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
This commit is contained in:
Radosław Kapka 2021-07-26 21:22:58 +02:00 committed by GitHub
parent 8c8f1bb9c1
commit 837cd4eb8f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 112 additions and 2 deletions

View File

@ -50,6 +50,7 @@ func (f *BeaconEndpointFactory) Paths() []string {
"/eth/v1/validator/duties/attester/{epoch}",
"/eth/v1/validator/duties/proposer/{epoch}",
"/eth/v1/validator/blocks/{slot}",
"/eth/v1/validator/attestation_data",
"/eth/v1/validator/aggregate_attestation",
}
}
@ -258,6 +259,13 @@ func (f *BeaconEndpointFactory) Create(path string) (*gateway.Endpoint, error) {
GetResponse: &produceBlockResponseJson{},
RequestURLLiterals: []string{"slot"},
RequestQueryParams: []gateway.QueryParam{{Name: "randao_reveal", Hex: true}, {Name: "graffiti", Hex: true}},
Err: &gateway.DefaultErrorJson{},
}
case "/eth/v1/validator/attestation_data":
endpoint = gateway.Endpoint{
GetResponse: &produceAttestationDataResponseJson{},
RequestQueryParams: []gateway.QueryParam{{Name: "slot"}, {Name: "committee_index"}},
Err: &gateway.DefaultErrorJson{},
}
case "/eth/v1/validator/aggregate_attestation":
endpoint = gateway.Endpoint{

View File

@ -196,6 +196,11 @@ type produceBlockResponseJson struct {
Data *beaconBlockJson `json:"data"`
}
// produceAttestationDataResponseJson is used in /validator/attestation_data API endpoint.
type produceAttestationDataResponseJson struct {
Data *attestationDataJson `json:"data"`
}
// aggregateAttestationResponseJson is used in /validator/aggregate_attestation API endpoint.
type aggregateAttestationResponseJson struct {
Data *attestationJson `json:"data"`

View File

@ -35,6 +35,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/state:go_default_library",
@ -42,6 +43,7 @@ go_test(
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/powchain/testing:go_default_library",
"//beacon-chain/rpc/prysm/v1alpha1/validator:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
@ -57,5 +59,6 @@ go_test(
"//shared/testutil/require:go_default_library",
"@com_github_prysmaticlabs_eth2_types//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)

View File

@ -184,7 +184,21 @@ func (vs *Server) ProduceBlock(ctx context.Context, req *v1.ProduceBlockRequest)
// ProduceAttestationData requests that the beacon node produces attestation data for
// the requested committee index and slot based on the nodes current head.
func (vs *Server) ProduceAttestationData(ctx context.Context, req *v1.ProduceAttestationDataRequest) (*v1.ProduceAttestationDataResponse, error) {
return nil, errors.New("Unimplemented")
ctx, span := trace.StartSpan(ctx, "validatorv1.ProduceAttestationData")
defer span.End()
v1alpha1req := &v1alpha1.AttestationDataRequest{
Slot: req.Slot,
CommitteeIndex: req.CommitteeIndex,
}
v1alpha1resp, err := vs.V1Alpha1Server.GetAttestationData(ctx, v1alpha1req)
if err != nil {
// We simply return err because it's already of a gRPC error type.
return nil, err
}
attData := migration.V1Alpha1AttDataToV1(v1alpha1resp)
return &v1.ProduceAttestationDataResponse{Data: attData}, nil
}
// GetAggregateAttestation aggregates all attestations matching the given attestation data root and slot, returning the aggregated result.

View File

@ -4,10 +4,12 @@ import (
"context"
"fmt"
"testing"
"time"
types "github.com/prysmaticlabs/eth2-types"
"github.com/prysmaticlabs/go-bitfield"
mockChain "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing"
"github.com/prysmaticlabs/prysm/beacon-chain/cache"
b "github.com/prysmaticlabs/prysm/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/state"
@ -15,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
mockp2p "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing"
mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
v1alpha1validator "github.com/prysmaticlabs/prysm/beacon-chain/rpc/prysm/v1alpha1/validator"
"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
@ -28,6 +31,7 @@ import (
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/testutil/assert"
"github.com/prysmaticlabs/prysm/shared/testutil/require"
"google.golang.org/protobuf/proto"
)
func TestGetAttesterDuties(t *testing.T) {
@ -314,7 +318,7 @@ func TestGetProposerDuties_SyncNotReady(t *testing.T) {
assert.ErrorContains(t, "Syncing to latest head, not ready to respond", err)
}
func TestGetBlock(t *testing.T) {
func TestProduceBlock(t *testing.T) {
db := dbutil.SetupDB(t)
ctx := context.Background()
@ -405,6 +409,82 @@ func TestGetBlock(t *testing.T) {
assert.DeepEqual(t, expectedAttSlashings, resp.Data.Body.AttesterSlashings)
}
func TestProduceAttestationData(t *testing.T) {
block := testutil.NewBeaconBlock()
block.Block.Slot = 3*params.BeaconConfig().SlotsPerEpoch + 1
targetBlock := testutil.NewBeaconBlock()
targetBlock.Block.Slot = 1 * params.BeaconConfig().SlotsPerEpoch
justifiedBlock := testutil.NewBeaconBlock()
justifiedBlock.Block.Slot = 2 * params.BeaconConfig().SlotsPerEpoch
blockRoot, err := block.Block.HashTreeRoot()
require.NoError(t, err, "Could not hash beacon block")
justifiedRoot, err := justifiedBlock.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root for justified block")
targetRoot, err := targetBlock.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root for target block")
slot := 3*params.BeaconConfig().SlotsPerEpoch + 1
beaconState, err := testutil.NewBeaconState()
require.NoError(t, err)
require.NoError(t, beaconState.SetSlot(slot))
err = beaconState.SetCurrentJustifiedCheckpoint(&ethpb.Checkpoint{
Epoch: 2,
Root: justifiedRoot[:],
})
require.NoError(t, err)
blockRoots := beaconState.BlockRoots()
blockRoots[1] = blockRoot[:]
blockRoots[1*params.BeaconConfig().SlotsPerEpoch] = targetRoot[:]
blockRoots[2*params.BeaconConfig().SlotsPerEpoch] = justifiedRoot[:]
require.NoError(t, beaconState.SetBlockRoots(blockRoots))
chainService := &mockChain.ChainService{
Genesis: time.Now(),
}
offset := int64(slot.Mul(params.BeaconConfig().SecondsPerSlot))
v1Alpha1Server := &v1alpha1validator.Server{
P2P: &mockp2p.MockBroadcaster{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
AttestationCache: cache.NewAttestationCache(),
HeadFetcher: &mockChain.ChainService{
State: beaconState, Root: blockRoot[:],
},
FinalizationFetcher: &mockChain.ChainService{
CurrentJustifiedCheckPoint: beaconState.CurrentJustifiedCheckpoint(),
},
TimeFetcher: &mockChain.ChainService{
Genesis: time.Now().Add(time.Duration(-1*offset) * time.Second),
},
StateNotifier: chainService.StateNotifier(),
}
v1Server := &Server{
V1Alpha1Server: v1Alpha1Server,
}
req := &v1.ProduceAttestationDataRequest{
CommitteeIndex: 0,
Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1,
}
res, err := v1Server.ProduceAttestationData(context.Background(), req)
require.NoError(t, err, "Could not get attestation info at slot")
expectedInfo := &v1.AttestationData{
Slot: 3*params.BeaconConfig().SlotsPerEpoch + 1,
BeaconBlockRoot: blockRoot[:],
Source: &v1.Checkpoint{
Epoch: 2,
Root: justifiedRoot[:],
},
Target: &v1.Checkpoint{
Epoch: 3,
Root: blockRoot[:],
},
}
if !proto.Equal(res.Data, expectedInfo) {
t.Errorf("Expected attestation info to match, received %v, wanted %v", res, expectedInfo)
}
}
func TestGetAggregateAttestation(t *testing.T) {
ctx := context.Background()
root1 := bytesutil.PadTo([]byte("root1"), 32)