mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-23 11:57:18 +00:00
Add REST implementation for validator's ProposeAttestation (#11800)
* Add REST implementation for validator's ProposeAttestation * handle nil attestation * update propose attestation with context * fix lint * add remaining nil testcases * Update validator/client/beacon-api/propose_attestation_test.go Co-authored-by: Radosław Kapka <radek@prysmaticlabs.com> * fix BUILD.bazel Co-authored-by: Radosław Kapka <radek@prysmaticlabs.com> Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
This commit is contained in:
parent
bbe003720c
commit
116f3ac265
@ -15,6 +15,7 @@ go_library(
|
||||
"index.go",
|
||||
"json_rest_handler.go",
|
||||
"prepare_beacon_proposer.go",
|
||||
"propose_attestation.go",
|
||||
"propose_beacon_block.go",
|
||||
"propose_exit.go",
|
||||
"registration.go",
|
||||
@ -64,6 +65,7 @@ go_test(
|
||||
"index_test.go",
|
||||
"json_rest_handler_test.go",
|
||||
"prepare_beacon_proposer_test.go",
|
||||
"propose_attestation_test.go",
|
||||
"propose_beacon_block_altair_test.go",
|
||||
"propose_beacon_block_bellatrix_test.go",
|
||||
"propose_beacon_block_blinded_bellatrix_test.go",
|
||||
|
@ -117,12 +117,7 @@ func (c *beaconApiValidatorClient) PrepareBeaconProposer(ctx context.Context, in
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ProposeAttestation(ctx context.Context, in *ethpb.Attestation) (*ethpb.AttestResponse, error) {
|
||||
if c.fallbackClient != nil {
|
||||
return c.fallbackClient.ProposeAttestation(ctx, in)
|
||||
}
|
||||
|
||||
// TODO: Implement me
|
||||
panic("beaconApiValidatorClient.ProposeAttestation is not implemented. To use a fallback client, create this validator with NewBeaconApiValidatorClientWithFallback instead.")
|
||||
return c.proposeAttestation(ctx, in)
|
||||
}
|
||||
|
||||
func (c *beaconApiValidatorClient) ProposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
|
||||
|
57
validator/client/beacon-api/propose_attestation.go
Normal file
57
validator/client/beacon-api/propose_attestation.go
Normal file
@ -0,0 +1,57 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
func (c beaconApiValidatorClient) proposeAttestation(ctx context.Context, attestation *ethpb.Attestation) (*ethpb.AttestResponse, error) {
|
||||
if err := checkNilAttestation(attestation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
marshalledAttestation, err := json.Marshal(jsonifyAttestations([]*ethpb.Attestation{attestation}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := c.jsonRestHandler.PostRestJson(ctx, "/eth/v1/beacon/pool/attestations", nil, bytes.NewBuffer(marshalledAttestation), nil); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to send POST data to REST endpoint")
|
||||
}
|
||||
|
||||
attestationDataRoot, err := attestation.Data.HashTreeRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to compute attestation data root")
|
||||
}
|
||||
|
||||
return ðpb.AttestResponse{AttestationDataRoot: attestationDataRoot[:]}, nil
|
||||
}
|
||||
|
||||
// checkNilAttestation returns error if attestation or any field of attestation is nil.
|
||||
func checkNilAttestation(attestation *ethpb.Attestation) error {
|
||||
if attestation == nil {
|
||||
return errors.New("attestation is nil")
|
||||
}
|
||||
|
||||
if attestation.Data == nil {
|
||||
return errors.New("attestation data is nil")
|
||||
}
|
||||
|
||||
if attestation.Data.Source == nil || attestation.Data.Target == nil {
|
||||
return errors.New("source/target in attestation data is nil")
|
||||
}
|
||||
|
||||
if len(attestation.AggregationBits) == 0 {
|
||||
return errors.New("attestation aggregation bits is empty")
|
||||
}
|
||||
|
||||
if len(attestation.Signature) == 0 {
|
||||
return errors.New("attestation signature is empty")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
156
validator/client/beacon-api/propose_attestation_test.go
Normal file
156
validator/client/beacon-api/propose_attestation_test.go
Normal file
@ -0,0 +1,156 @@
|
||||
package beacon_api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/assert"
|
||||
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
||||
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
|
||||
test_helpers "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/test-helpers"
|
||||
)
|
||||
|
||||
func TestProposeAttestation(t *testing.T) {
|
||||
attestation := ðpb.Attestation{
|
||||
AggregationBits: test_helpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 75,
|
||||
CommitteeIndex: 76,
|
||||
BeaconBlockRoot: test_helpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 78,
|
||||
Root: test_helpers.FillByteSlice(32, 79),
|
||||
},
|
||||
Target: ðpb.Checkpoint{
|
||||
Epoch: 80,
|
||||
Root: test_helpers.FillByteSlice(32, 81),
|
||||
},
|
||||
},
|
||||
Signature: test_helpers.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: test_helpers.FillByteSlice(4, 74),
|
||||
Signature: test_helpers.FillByteSlice(96, 82),
|
||||
},
|
||||
expectedErrorMessage: "attestation data is nil",
|
||||
},
|
||||
{
|
||||
name: "nil source checkpoint",
|
||||
attestation: ðpb.Attestation{
|
||||
AggregationBits: test_helpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Target: ðpb.Checkpoint{},
|
||||
},
|
||||
Signature: test_helpers.FillByteSlice(96, 82),
|
||||
},
|
||||
expectedErrorMessage: "source/target in attestation data is nil",
|
||||
},
|
||||
{
|
||||
name: "nil target checkpoint",
|
||||
attestation: ðpb.Attestation{
|
||||
AggregationBits: test_helpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{},
|
||||
},
|
||||
Signature: test_helpers.FillByteSlice(96, 82),
|
||||
},
|
||||
expectedErrorMessage: "source/target in attestation data is nil",
|
||||
},
|
||||
{
|
||||
name: "nil aggregation bits",
|
||||
attestation: ðpb.Attestation{
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{},
|
||||
Target: ðpb.Checkpoint{},
|
||||
},
|
||||
Signature: test_helpers.FillByteSlice(96, 82),
|
||||
},
|
||||
expectedErrorMessage: "attestation aggregation bits is empty",
|
||||
},
|
||||
{
|
||||
name: "nil signature",
|
||||
attestation: ðpb.Attestation{
|
||||
AggregationBits: test_helpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Source: ðpb.Checkpoint{},
|
||||
Target: ðpb.Checkpoint{},
|
||||
},
|
||||
},
|
||||
expectedErrorMessage: "attestation signature is empty",
|
||||
},
|
||||
{
|
||||
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 checkNilAttestation(test.attestation) == nil {
|
||||
b, err := json.Marshal(jsonifyAttestations([]*ethpb.Attestation{test.attestation}))
|
||||
require.NoError(t, err)
|
||||
marshalledAttestations = b
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
jsonRestHandler.EXPECT().PostRestJson(
|
||||
ctx,
|
||||
"/eth/v1/beacon/pool/attestations",
|
||||
nil,
|
||||
bytes.NewBuffer(marshalledAttestations),
|
||||
nil,
|
||||
).Return(
|
||||
nil,
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user