Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add REST implementation for Validator's ProposeBeaconBlock #11731

Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ad131d8
WIP
PatriceVignola Dec 4, 2022
284a71d
WIP
PatriceVignola Dec 4, 2022
00bf3f5
WIP
PatriceVignola Dec 4, 2022
053ad8b
Add tests
PatriceVignola Dec 5, 2022
80d10e8
WIP
PatriceVignola Dec 5, 2022
3fa8482
Merge branch 'develop' of github.com:prysmaticlabs/prysm into user/pa…
PatriceVignola Dec 5, 2022
b9924a6
Add more tests
PatriceVignola Dec 5, 2022
c3772df
Address DeepSource errors
PatriceVignola Dec 5, 2022
edd9ed2
Remove unused param
PatriceVignola Dec 5, 2022
e8717cf
Merge branch 'develop' of github.com:prysmaticlabs/prysm into user/pa…
PatriceVignola Dec 7, 2022
0b306ef
Add more tests
PatriceVignola Dec 7, 2022
316ba3a
Merge branch 'develop' of github.com:prysmaticlabs/prysm into user/pa…
PatriceVignola Dec 7, 2022
4c19b62
Address PR comments
PatriceVignola Dec 7, 2022
87ffc35
Merge branch 'develop' into user/pavignol/add-propose-beacon-block
rkapka Dec 8, 2022
8d4eb5b
Address PR comments
PatriceVignola Dec 8, 2022
f42cc1d
Merge branch 'develop' into user/pavignol/add-propose-beacon-block
PatriceVignola Dec 8, 2022
b1722bf
Fix formatting
PatriceVignola Dec 8, 2022
79c3e20
Remove unused parameter
PatriceVignola Dec 8, 2022
345274a
Merge branch 'develop' into user/pavignol/add-propose-beacon-block
rkapka Dec 9, 2022
0f94fe4
Fix TestLittleEndianBytesToBigInt
PatriceVignola Dec 9, 2022
0baed28
Merge branch 'develop' into user/pavignol/add-propose-beacon-block
PatriceVignola Dec 9, 2022
3c6a969
Merge branch 'develop' into user/pavignol/add-propose-beacon-block
PatriceVignola Dec 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions api/client/builder/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ func stringToUint256(s string) (Uint256, error) {

// sszBytesToUint256 creates a Uint256 from a ssz-style (little-endian byte slice) representation.
func sszBytesToUint256(b []byte) (Uint256, error) {
bi := new(big.Int)
bi.SetBytes(bytesutil.ReverseByteOrder(b))
bi := bytesutil.LittleEndianBytesToBigInt(b)
if !isValidUint256(bi) {
return Uint256{}, errors.Wrapf(errDecodeUint256, "value=%s", b)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bytes"
"context"
"fmt"
"math/big"
"time"

"github.com/pkg/errors"
Expand Down Expand Up @@ -129,7 +128,7 @@ func (vs *Server) getPayloadHeaderFromBuilder(ctx context.Context, slot types.Sl
return nil, errors.New("builder returned nil bid")
}

v := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(bid.Message.Value))
v := bytesutil.LittleEndianBytesToBigInt(bid.Message.Value)
if v.String() == "0" {
return nil, errors.New("builder returned header with 0 bid amount")
}
Expand Down
7 changes: 7 additions & 0 deletions encoding/bytesutil/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package bytesutil
import (
"encoding/binary"
"fmt"
"math/big"
"math/bits"
"regexp"

Expand Down Expand Up @@ -438,3 +439,9 @@ func IsRoot(root []byte) bool {
func IsValidRoot(root []byte) bool {
return IsRoot(root) && !ZeroRoot(root)
}

// LittleEndianBytesToBigInt takes bytes of a number stored as little-endian and returns a big integer
func LittleEndianBytesToBigInt(bytes []byte) *big.Int {
// Integers are stored as little-endian, but big.Int expects big-endian. So we need to reverse the byte order before decoding.
return new(big.Int).SetBytes(ReverseByteOrder(bytes))
}
8 changes: 8 additions & 0 deletions encoding/bytesutil/bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package bytesutil_test

import (
"bytes"
"encoding/binary"
"fmt"
"reflect"
"testing"
Expand Down Expand Up @@ -634,3 +635,10 @@ func TestToBytes48Array(t *testing.T) {
assert.DeepEqual(t, tt.b, b)
}
}

func TestLittleEndianBytesToBigInt(t *testing.T) {
bytes := make([]byte, 8)
binary.LittleEndian.PutUint64(bytes, 1234567890)
converted := bytesutil.LittleEndianBytesToBigInt(bytes)
assert.Equal(t, 1234567890, converted)
}
2 changes: 1 addition & 1 deletion proto/engine/v1/json_marshal_unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (e *ExecutionPayload) MarshalJSON() ([]byte, error) {
for i, tx := range e.Transactions {
transactions[i] = tx
}
baseFee := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(e.BaseFeePerGas))
baseFee := bytesutil.LittleEndianBytesToBigInt(e.BaseFeePerGas)
baseFeeHex := hexutil.EncodeBig(baseFee)
pHash := common.BytesToHash(e.ParentHash)
sRoot := common.BytesToHash(e.StateRoot)
Expand Down
4 changes: 1 addition & 3 deletions runtime/interop/generate_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,7 @@ func deterministicallyGenerateKeys(startIndex, numKeys uint64) ([]bls.SecretKey,
binary.LittleEndian.PutUint32(enc, uint32(i))
h := hash.Hash(enc)
// Reverse byte order to big endian for use with big ints.
b := bytesutil.ReverseByteOrder(h[:])
num := new(big.Int)
num = num.SetBytes(b)
num := bytesutil.LittleEndianBytesToBigInt(h[:])
order := new(big.Int)
var ok bool
order, ok = order.SetString(bls.CurveOrder, 10)
Expand Down
3 changes: 1 addition & 2 deletions testing/spectest/shared/common/forkchoice/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package forkchoice

import (
"context"
"math/big"
"testing"

"github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -107,7 +106,7 @@ func (m *engineMock) ExecutionBlockByHash(_ context.Context, hash common.Hash, _
return nil, nil
}

td := new(big.Int).SetBytes(bytesutil.ReverseByteOrder(b.TotalDifficulty))
td := bytesutil.LittleEndianBytesToBigInt(b.TotalDifficulty)
tdHex := hexutil.EncodeBig(td)
return &pb.ExecutionBlock{
Header: gethtypes.Header{
Expand Down
10 changes: 10 additions & 0 deletions validator/client/beacon-api/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ go_library(
"attestation_data.go",
"beacon_api_helpers.go",
"beacon_api_validator_client.go",
"beacon_block_json_helpers.go",
"domain_data.go",
"genesis.go",
"index.go",
"json_rest_handler.go",
"propose_beacon_block.go",
"state_validators.go",
],
importpath = "github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api",
Expand Down Expand Up @@ -40,10 +42,17 @@ go_test(
"attestation_data_test.go",
"beacon_api_helpers_test.go",
"beacon_api_validator_client_test.go",
"beacon_block_json_helpers_test.go",
"domain_data_test.go",
"genesis_test.go",
"index_test.go",
"json_rest_handler_test.go",
"propose_beacon_block_altair_test.go",
"propose_beacon_block_bellatrix_test.go",
"propose_beacon_block_blinded_bellatrix_test.go",
"propose_beacon_block_blinded_capella_test.go",
"propose_beacon_block_phase0_test.go",
"propose_beacon_block_test.go",
"state_validators_test.go",
"wait_for_chain_start_test.go",
],
Expand All @@ -54,6 +63,7 @@ go_test(
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
Expand Down
7 changes: 7 additions & 0 deletions validator/client/beacon-api/beacon_api_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"fmt"
neturl "net/url"
"regexp"
"strconv"

types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
)

func validRoot(root string) bool {
Expand All @@ -14,6 +17,10 @@ func validRoot(root string) bool {
return matchesRegex
}

func uint64ToString[T uint64 | types.Slot | types.ValidatorIndex | types.CommitteeIndex | types.Epoch](val T) string {
return strconv.FormatUint(uint64(val), 10)
}

func buildURL(path string, queryParams ...neturl.Values) string {
if len(queryParams) == 0 {
return path
Expand Down
12 changes: 12 additions & 0 deletions validator/client/beacon-api/beacon_api_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"net/url"
"testing"

types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/testing/assert"
)

Expand Down Expand Up @@ -52,6 +53,17 @@ func TestBeaconApiHelpers(t *testing.T) {
}
}

func TestBeaconApiHelpers_TestUint64ToString(t *testing.T) {
const expectedResult = "1234"
const val = uint64(1234)

assert.Equal(t, expectedResult, uint64ToString(val))
assert.Equal(t, expectedResult, uint64ToString(types.Slot(val)))
assert.Equal(t, expectedResult, uint64ToString(types.ValidatorIndex(val)))
assert.Equal(t, expectedResult, uint64ToString(types.CommitteeIndex(val)))
assert.Equal(t, expectedResult, uint64ToString(types.Epoch(val)))
}

func TestBuildURL_NoParams(t *testing.T) {
wanted := "/aaa/bbb/ccc"
actual := buildURL("/aaa/bbb/ccc")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,7 @@ func (c *beaconApiValidatorClient) ProposeAttestation(ctx context.Context, in *e
}

func (c *beaconApiValidatorClient) ProposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
if c.fallbackClient != nil {
return c.fallbackClient.ProposeBeaconBlock(ctx, in)
}

// TODO: Implement me
panic("beaconApiValidatorClient.ProposeBeaconBlock is not implemented. To use a fallback client, create this validator with NewBeaconApiValidatorClientWithFallback instead.")
return c.proposeBeaconBlock(in)
}

func (c *beaconApiValidatorClient) ProposeExit(ctx context.Context, in *ethpb.SignedVoluntaryExit) (*ethpb.ProposeExitResponse, error) {
Expand Down
102 changes: 86 additions & 16 deletions validator/client/beacon-api/beacon_api_validator_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,46 @@ import (
"github.com/prysmaticlabs/prysm/v3/validator/client/beacon-api/mock"
)

// Make sure that GetAttestationData() returns the same thing as the internal getAttestationData()
func TestBeaconApiValidatorClient_GetAttestationDataValid(t *testing.T) {
const slot = types.Slot(1)
const committeeIndex = types.CommitteeIndex(2)

ctrl := gomock.NewController(t)
defer ctrl.Finish()

jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
produceAttestationDataResponseJson := rpcmiddleware.ProduceAttestationDataResponseJson{}
jsonRestHandler.EXPECT().GetRestJsonResponse(
fmt.Sprintf("/eth/v1/validator/attestation_data?committee_index=%d&slot=%d", committeeIndex, slot),
&produceAttestationDataResponseJson,
).Return(
nil,
nil,
).SetArg(
1,
generateValidAttestation(uint64(slot), uint64(committeeIndex)),
).Times(2)

validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
expectedResp, expectedErr := validatorClient.getAttestationData(slot, committeeIndex)

resp, err := validatorClient.GetAttestationData(
context.Background(),
&ethpb.AttestationDataRequest{Slot: slot, CommitteeIndex: committeeIndex},
)

assert.DeepEqual(t, expectedErr, err)
assert.DeepEqual(t, expectedResp, resp)
}

func TestBeaconApiValidatorClient_GetAttestationDataNilInput(t *testing.T) {
validatorClient := beaconApiValidatorClient{}
_, err := validatorClient.GetAttestationData(context.Background(), nil)
assert.ErrorContains(t, "GetAttestationData received nil argument `in`", err)
}

// Make sure that GetAttestationData() returns the same thing as the internal getAttestationData()
func TestBeaconApiValidatorClient_GetAttestationDataValid(t *testing.T) {
func TestBeaconApiValidatorClient_GetAttestationDataError(t *testing.T) {
const slot = types.Slot(1)
const committeeIndex = types.CommitteeIndex(2)

Expand All @@ -39,7 +71,7 @@ func TestBeaconApiValidatorClient_GetAttestationDataValid(t *testing.T) {
&produceAttestationDataResponseJson,
).Return(
nil,
nil,
errors.New("some specific json error"),
).SetArg(
1,
generateValidAttestation(uint64(slot), uint64(committeeIndex)),
Expand All @@ -53,7 +85,7 @@ func TestBeaconApiValidatorClient_GetAttestationDataValid(t *testing.T) {
&ethpb.AttestationDataRequest{Slot: slot, CommitteeIndex: committeeIndex},
)

assert.DeepEqual(t, expectedErr, err)
assert.ErrorContains(t, expectedErr.Error(), err)
assert.DeepEqual(t, expectedResp, resp)
}

Expand Down Expand Up @@ -89,32 +121,70 @@ func TestBeaconApiValidatorClient_DomainDataError(t *testing.T) {
assert.ErrorContains(t, fmt.Sprintf("invalid domain type: %s", hexutil.Encode(domainType)), err)
}

func TestBeaconApiValidatorClient_GetAttestationDataError(t *testing.T) {
func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)

jsonRestHandler.EXPECT().PostRestJson(
"/eth/v1/beacon/blocks",
map[string]string{"Eth-Consensus-Version": "phase0"},
gomock.Any(),
nil,
).Return(
nil,
rkapka marked this conversation as resolved.
Show resolved Hide resolved
nil,
).Times(2)

validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
)

resp, err := validatorClient.ProposeBeaconBlock(
context.Background(),
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
)

assert.DeepEqual(t, expectedErr, err)
assert.DeepEqual(t, expectedResp, resp)
}

func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
const slot = types.Slot(1)
const committeeIndex = types.CommitteeIndex(2)

ctrl := gomock.NewController(t)
defer ctrl.Finish()

jsonRestHandler := mock.NewMockjsonRestHandler(ctrl)
produceAttestationDataResponseJson := rpcmiddleware.ProduceAttestationDataResponseJson{}
jsonRestHandler.EXPECT().GetRestJsonResponse(
fmt.Sprintf("/eth/v1/validator/attestation_data?committee_index=%d&slot=%d", committeeIndex, slot),
&produceAttestationDataResponseJson,
jsonRestHandler.EXPECT().PostRestJson(
"/eth/v1/beacon/blocks",
map[string]string{"Eth-Consensus-Version": "phase0"},
gomock.Any(),
nil,
).Return(
nil,
errors.New("some specific json error"),
).SetArg(
1,
generateValidAttestation(uint64(slot), uint64(committeeIndex)),
errors.New("foo error"),
).Times(2)

validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
expectedResp, expectedErr := validatorClient.getAttestationData(slot, committeeIndex)
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
)

resp, err := validatorClient.GetAttestationData(
resp, err := validatorClient.ProposeBeaconBlock(
context.Background(),
&ethpb.AttestationDataRequest{Slot: slot, CommitteeIndex: committeeIndex},
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
)

assert.ErrorContains(t, expectedErr.Error(), err)
Expand Down
Loading