mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
* Fix incorrect version used when sending attestation version in Fulu * update typo * fix Eth-Consensus-Version in submit_signed_aggregate_proof.go
367 lines
11 KiB
Go
367 lines
11 KiB
Go
package beacon_api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"testing"
|
|
|
|
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
|
|
"github.com/OffchainLabs/prysm/v6/config/params"
|
|
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
|
|
"github.com/OffchainLabs/prysm/v6/network/httputil"
|
|
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
|
|
"github.com/OffchainLabs/prysm/v6/runtime/version"
|
|
"github.com/OffchainLabs/prysm/v6/testing/assert"
|
|
"github.com/OffchainLabs/prysm/v6/testing/require"
|
|
"github.com/OffchainLabs/prysm/v6/time/slots"
|
|
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
|
|
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
|
|
"go.uber.org/mock/gomock"
|
|
)
|
|
|
|
func TestProposeAttestation(t *testing.T) {
|
|
attestation := ðpb.Attestation{
|
|
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
|
Data: ðpb.AttestationData{
|
|
Slot: 75,
|
|
CommitteeIndex: 76,
|
|
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 78,
|
|
Root: testhelpers.FillByteSlice(32, 79),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 80,
|
|
Root: testhelpers.FillByteSlice(32, 81),
|
|
},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
attestation *ethpb.Attestation
|
|
expectedErrorMessage string
|
|
endpointError error
|
|
endpointCall int
|
|
}{
|
|
{
|
|
name: "valid",
|
|
attestation: attestation,
|
|
endpointCall: 1,
|
|
},
|
|
{
|
|
name: "nil attestation",
|
|
expectedErrorMessage: "attestation is nil",
|
|
},
|
|
{
|
|
name: "nil attestation data",
|
|
attestation: ðpb.Attestation{
|
|
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
},
|
|
expectedErrorMessage: "attestation is nil",
|
|
},
|
|
{
|
|
name: "nil source checkpoint",
|
|
attestation: ðpb.Attestation{
|
|
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
|
Data: ðpb.AttestationData{
|
|
Target: ðpb.Checkpoint{},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
},
|
|
expectedErrorMessage: "attestation's source can't be nil",
|
|
},
|
|
{
|
|
name: "nil target checkpoint",
|
|
attestation: ðpb.Attestation{
|
|
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
|
Data: ðpb.AttestationData{
|
|
Source: ðpb.Checkpoint{},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
},
|
|
expectedErrorMessage: "attestation's target can't be nil",
|
|
},
|
|
{
|
|
name: "nil aggregation bits",
|
|
attestation: ðpb.Attestation{
|
|
Data: ðpb.AttestationData{
|
|
Source: ðpb.Checkpoint{},
|
|
Target: ðpb.Checkpoint{},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
},
|
|
expectedErrorMessage: "attestation's bitfield can't be nil",
|
|
},
|
|
{
|
|
name: "bad request",
|
|
attestation: attestation,
|
|
expectedErrorMessage: "bad request",
|
|
endpointError: errors.New("bad request"),
|
|
endpointCall: 1,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
var marshalledAttestations []byte
|
|
if helpers.ValidateNilAttestation(test.attestation) == nil {
|
|
b, err := json.Marshal(jsonifyAttestations([]*ethpb.Attestation{test.attestation}))
|
|
require.NoError(t, err)
|
|
marshalledAttestations = b
|
|
}
|
|
|
|
ctx := t.Context()
|
|
|
|
headers := map[string]string{"Eth-Consensus-Version": version.String(test.attestation.Version())}
|
|
jsonRestHandler.EXPECT().Post(
|
|
gomock.Any(),
|
|
"/eth/v2/beacon/pool/attestations",
|
|
headers,
|
|
bytes.NewBuffer(marshalledAttestations),
|
|
nil,
|
|
).Return(
|
|
test.endpointError,
|
|
).Times(test.endpointCall)
|
|
|
|
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
|
proposeResponse, err := validatorClient.proposeAttestation(ctx, test.attestation)
|
|
if test.expectedErrorMessage != "" {
|
|
require.ErrorContains(t, test.expectedErrorMessage, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, proposeResponse)
|
|
|
|
expectedAttestationDataRoot, err := attestation.Data.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
// Make sure that the attestation data root is set
|
|
assert.DeepEqual(t, expectedAttestationDataRoot[:], proposeResponse.AttestationDataRoot)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProposeAttestationFallBack(t *testing.T) {
|
|
attestation := ðpb.Attestation{
|
|
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
|
Data: ðpb.AttestationData{
|
|
Slot: 75,
|
|
CommitteeIndex: 76,
|
|
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: 78,
|
|
Root: testhelpers.FillByteSlice(32, 79),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: 80,
|
|
Root: testhelpers.FillByteSlice(32, 81),
|
|
},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
}
|
|
|
|
ctrl := gomock.NewController(t)
|
|
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
var marshalledAttestations []byte
|
|
if helpers.ValidateNilAttestation(attestation) == nil {
|
|
b, err := json.Marshal(jsonifyAttestations([]*ethpb.Attestation{attestation}))
|
|
require.NoError(t, err)
|
|
marshalledAttestations = b
|
|
}
|
|
|
|
ctx := t.Context()
|
|
headers := map[string]string{"Eth-Consensus-Version": version.String(attestation.Version())}
|
|
jsonRestHandler.EXPECT().Post(
|
|
gomock.Any(),
|
|
"/eth/v2/beacon/pool/attestations",
|
|
headers,
|
|
bytes.NewBuffer(marshalledAttestations),
|
|
nil,
|
|
).Return(
|
|
&httputil.DefaultJsonError{
|
|
Code: http.StatusNotFound,
|
|
},
|
|
).Times(1)
|
|
|
|
jsonRestHandler.EXPECT().Post(
|
|
gomock.Any(),
|
|
"/eth/v1/beacon/pool/attestations",
|
|
nil,
|
|
bytes.NewBuffer(marshalledAttestations),
|
|
nil,
|
|
).Return(
|
|
nil,
|
|
).Times(1)
|
|
|
|
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
|
proposeResponse, err := validatorClient.proposeAttestation(ctx, attestation)
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, proposeResponse)
|
|
|
|
expectedAttestationDataRoot, err := attestation.Data.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
// Make sure that the attestation data root is set
|
|
assert.DeepEqual(t, expectedAttestationDataRoot[:], proposeResponse.AttestationDataRoot)
|
|
}
|
|
|
|
func TestProposeAttestationElectra(t *testing.T) {
|
|
params.SetupTestConfigCleanup(t)
|
|
params.BeaconConfig().ElectraForkEpoch = 0
|
|
params.BeaconConfig().FuluForkEpoch = 1
|
|
|
|
buildSingleAttestation := func(slot primitives.Slot) *ethpb.SingleAttestation {
|
|
targetEpoch := slots.ToEpoch(slot)
|
|
sourceEpoch := targetEpoch
|
|
if targetEpoch > 0 {
|
|
sourceEpoch = targetEpoch - 1
|
|
}
|
|
return ðpb.SingleAttestation{
|
|
AttesterIndex: 74,
|
|
Data: ðpb.AttestationData{
|
|
Slot: slot,
|
|
CommitteeIndex: 76,
|
|
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
|
Source: ðpb.Checkpoint{
|
|
Epoch: sourceEpoch,
|
|
Root: testhelpers.FillByteSlice(32, 79),
|
|
},
|
|
Target: ðpb.Checkpoint{
|
|
Epoch: targetEpoch,
|
|
Root: testhelpers.FillByteSlice(32, 81),
|
|
},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
CommitteeId: 83,
|
|
}
|
|
}
|
|
|
|
attestationElectra := buildSingleAttestation(0)
|
|
attestationFulu := buildSingleAttestation(params.BeaconConfig().SlotsPerEpoch)
|
|
|
|
tests := []struct {
|
|
name string
|
|
attestation *ethpb.SingleAttestation
|
|
expectedConsensusVersion string
|
|
expectedErrorMessage string
|
|
endpointError error
|
|
endpointCall int
|
|
}{
|
|
{
|
|
name: "valid electra",
|
|
attestation: attestationElectra,
|
|
expectedConsensusVersion: version.String(slots.ToForkVersion(attestationElectra.GetData().GetSlot())),
|
|
endpointCall: 1,
|
|
},
|
|
{
|
|
name: "valid fulu consensus version",
|
|
attestation: attestationFulu,
|
|
expectedConsensusVersion: version.String(slots.ToForkVersion(attestationFulu.GetData().GetSlot())),
|
|
endpointCall: 1,
|
|
},
|
|
{
|
|
name: "nil attestation",
|
|
expectedErrorMessage: "attestation is nil",
|
|
},
|
|
{
|
|
name: "nil attestation data",
|
|
attestation: ðpb.SingleAttestation{
|
|
AttesterIndex: 74,
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
CommitteeId: 83,
|
|
},
|
|
expectedErrorMessage: "attestation is nil",
|
|
},
|
|
{
|
|
name: "nil source checkpoint",
|
|
attestation: ðpb.SingleAttestation{
|
|
AttesterIndex: 74,
|
|
Data: ðpb.AttestationData{
|
|
Target: ðpb.Checkpoint{},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
CommitteeId: 83,
|
|
},
|
|
expectedErrorMessage: "attestation's source can't be nil",
|
|
},
|
|
{
|
|
name: "nil target checkpoint",
|
|
attestation: ðpb.SingleAttestation{
|
|
AttesterIndex: 74,
|
|
Data: ðpb.AttestationData{
|
|
Source: ðpb.Checkpoint{},
|
|
},
|
|
Signature: testhelpers.FillByteSlice(96, 82),
|
|
CommitteeId: 83,
|
|
},
|
|
expectedErrorMessage: "attestation's target can't be nil",
|
|
},
|
|
{
|
|
name: "bad request",
|
|
attestation: attestationElectra,
|
|
expectedConsensusVersion: version.String(
|
|
slots.ToForkVersion(attestationElectra.GetData().GetSlot()),
|
|
),
|
|
expectedErrorMessage: "bad request",
|
|
endpointError: errors.New("bad request"),
|
|
endpointCall: 1,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
|
|
|
|
var marshalledAttestations []byte
|
|
if helpers.ValidateNilAttestation(test.attestation) == nil {
|
|
b, err := json.Marshal(jsonifySingleAttestations([]*ethpb.SingleAttestation{test.attestation}))
|
|
require.NoError(t, err)
|
|
marshalledAttestations = b
|
|
}
|
|
|
|
ctx := t.Context()
|
|
headerMatcher := gomock.Any()
|
|
if test.expectedConsensusVersion != "" {
|
|
headerMatcher = gomock.Eq(map[string]string{"Eth-Consensus-Version": test.expectedConsensusVersion})
|
|
}
|
|
jsonRestHandler.EXPECT().Post(
|
|
gomock.Any(),
|
|
"/eth/v2/beacon/pool/attestations",
|
|
headerMatcher,
|
|
bytes.NewBuffer(marshalledAttestations),
|
|
nil,
|
|
).Return(
|
|
test.endpointError,
|
|
).Times(test.endpointCall)
|
|
|
|
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
|
|
proposeResponse, err := validatorClient.proposeAttestationElectra(ctx, test.attestation)
|
|
if test.expectedErrorMessage != "" {
|
|
require.ErrorContains(t, test.expectedErrorMessage, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
require.NotNil(t, proposeResponse)
|
|
|
|
expectedAttestationDataRoot, err := test.attestation.Data.HashTreeRoot()
|
|
require.NoError(t, err)
|
|
|
|
// Make sure that the attestation data root is set
|
|
assert.DeepEqual(t, expectedAttestationDataRoot[:], proposeResponse.AttestationDataRoot)
|
|
})
|
|
}
|
|
}
|