From 4bf41f6066dd12171a862b2e4e8c800447ad4894 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 20 Jul 2026 09:18:22 -0500 Subject: [PATCH 01/19] poc jit builder settings --- beacon-chain/rpc/endpoints.go | 2 +- beacon-chain/rpc/endpoints_test.go | 2 +- beacon-chain/rpc/eth/validator/BUILD.bazel | 1 + .../rpc/eth/validator/builder_preferences.go | 107 + .../rpc/eth/validator/handlers_block_gloas.go | 12 + .../rpc/prysm/v1alpha1/validator/BUILD.bazel | 1 + .../v1alpha1/validator/builder_preferences.go | 32 - .../validator/builder_preferences_test.go | 61 - .../rpc/prysm/v1alpha1/validator/proposer.go | 6 +- .../prysm/v1alpha1/validator/proposer_bid.go | 51 +- .../validator/proposer_bid_builder_test.go | 4 +- .../validator/proposer_bid_prefs_test.go | 117 + .../v1alpha1/validator/proposer_bid_test.go | 16 +- .../v1alpha1/validator/proposer_gloas.go | 45 +- .../rpc/prysm/v1alpha1/validator/server.go | 1 - .../james-prysm_validator-config-endpoint.md | 8 + cmd/prysmctl/validator/proposer_settings.go | 3 +- config/proposer/BUILD.bazel | 1 + config/proposer/loader/BUILD.bazel | 1 + config/proposer/loader/loader.go | 7 +- config/proposer/loader/loader_test.go | 76 +- config/proposer/settings.go | 144 +- config/proposer/settings_test.go | 52 +- proto/prysm/v1alpha1/gloas_builder_api.pb.go | 98 +- proto/prysm/v1alpha1/gloas_builder_api.proto | 16 +- .../validator-client/keymanager.pb.go | 438 +++- .../validator-client/keymanager.proto | 35 +- proto/prysm/v1alpha1/validator.pb.go | 2014 ++++++++--------- proto/prysm/v1alpha1/validator.proto | 20 +- testing/mock/beacon_validator_client_mock.go | 20 - testing/mock/beacon_validator_server_mock.go | 15 - .../validator-mock/validator_client_mock.go | 15 - validator/client/BUILD.bazel | 2 + validator/client/beacon-api/BUILD.bazel | 2 + .../beacon-api/beacon_api_validator_client.go | 8 +- .../client/beacon-api/builder_preferences.go | 59 + .../client/beacon-api/get_beacon_block.go | 27 +- .../beacon-api/get_beacon_block_test.go | 68 +- .../client/grpc-api/grpc_validator_client.go | 3 - validator/client/iface/validator_client.go | 1 - validator/client/propose.go | 17 +- validator/client/runner_test.go | 14 +- validator/client/validator.go | 144 +- validator/client/validator_test.go | 52 +- validator/db/BUILD.bazel | 1 + validator/db/convert_test.go | 8 +- validator/db/kv/BUILD.bazel | 1 + validator/db/kv/proposer_settings_test.go | 16 +- validator/rpc/BUILD.bazel | 3 + validator/rpc/handlers_keymanager.go | 8 + validator/rpc/handlers_keymanager_test.go | 18 +- validator/rpc/handlers_validator_config.go | 374 +++ .../rpc/handlers_validator_config_test.go | 231 ++ validator/rpc/server.go | 4 + validator/rpc/structs.go | 50 + 55 files changed, 2914 insertions(+), 1618 deletions(-) create mode 100644 beacon-chain/rpc/eth/validator/builder_preferences.go delete mode 100644 beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go delete mode 100644 beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go create mode 100644 beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go create mode 100644 changelog/james-prysm_validator-config-endpoint.md create mode 100644 validator/client/beacon-api/builder_preferences.go create mode 100644 validator/rpc/handlers_validator_config.go create mode 100644 validator/rpc/handlers_validator_config_test.go diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index 0d1fe6dfd954..ccf07cdb14a9 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -418,7 +418,7 @@ func (s *Service) validatorEndpoints( middleware.AcceptEncodingHeaderHandler(), }, handler: server.ProduceBlockV4, - methods: []string{http.MethodGet}, + methods: []string{http.MethodGet, http.MethodPost}, }, { template: "/eth/v1/validator/beacon_committee_selections", diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index 6032859dd32e..239611b7ac8a 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -103,7 +103,7 @@ func Test_endpoints(t *testing.T) { "/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost}, "/eth/v1/validator/duties/ptc/{epoch}": {http.MethodPost}, "/eth/v3/validator/blocks/{slot}": {http.MethodGet}, - "/eth/v4/validator/blocks/{slot}": {http.MethodGet}, + "/eth/v4/validator/blocks/{slot}": {http.MethodGet, http.MethodPost}, "/eth/v1/validator/attestation_data": {http.MethodGet}, "/eth/v2/validator/aggregate_attestation": {http.MethodGet}, "/eth/v2/validator/aggregate_and_proofs": {http.MethodPost}, diff --git a/beacon-chain/rpc/eth/validator/BUILD.bazel b/beacon-chain/rpc/eth/validator/BUILD.bazel index 5013535e4725..9b461f5771af 100644 --- a/beacon-chain/rpc/eth/validator/BUILD.bazel +++ b/beacon-chain/rpc/eth/validator/BUILD.bazel @@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", srcs = [ + "builder_preferences.go", "handlers.go", "handlers_block.go", "handlers_block_gloas.go", diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go new file mode 100644 index 000000000000..ce68e4296278 --- /dev/null +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -0,0 +1,107 @@ +package validator + +import ( + "encoding/json" + "io" + "net/http" + "strconv" + + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" +) + +// produceBlockV4Request is the POST body for the JIT produce-block endpoint +// (beacon-APIs #625): the proposer's per-builder preferences for this proposal. +type produceBlockV4Request struct { + BuilderPreferences []*builderPreferenceJson `json:"builder_preferences"` +} + +type builderPreferenceJson struct { + SignedRequestAuth *signedRequestAuthJson `json:"signed_request_auth"` + MaxExecutionPayment string `json:"max_execution_payment"` + MinBid string `json:"min_bid,omitempty"` + BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` +} + +type signedRequestAuthJson struct { + Message *requestAuthJson `json:"message"` + Signature string `json:"signature"` +} + +type requestAuthJson struct { + Data string `json:"data"` + Slot string `json:"slot"` +} + +// parseBuilderPreferencesBody decodes the JIT builder-preferences POST body into +// consensus objects. An empty body yields no preferences. +func parseBuilderPreferencesBody(r *http.Request) ([]*eth.BuilderPreferenceV1, error) { + if r.Body == nil { + return nil, nil + } + var req produceBlockV4Request + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + if errors.Is(err, io.EOF) { + return nil, nil + } + return nil, errors.Wrap(err, "could not decode builder preferences") + } + out := make([]*eth.BuilderPreferenceV1, 0, len(req.BuilderPreferences)) + for i, p := range req.BuilderPreferences { + conv, err := p.toConsensus(i) + if err != nil { + return nil, err + } + out = append(out, conv) + } + return out, nil +} + +func (p *builderPreferenceJson) toConsensus(i int) (*eth.BuilderPreferenceV1, error) { + if p == nil || p.SignedRequestAuth == nil || p.SignedRequestAuth.Message == nil { + return nil, errors.Errorf("builder_preferences[%d] missing signed_request_auth", i) + } + maxPayment, err := strconv.ParseUint(p.MaxExecutionPayment, 10, 64) + if err != nil { + return nil, errors.Errorf("builder_preferences[%d].max_execution_payment is not a valid uint64", i) + } + data, err := hexutil.Decode(p.SignedRequestAuth.Message.Data) + if err != nil { + return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.message.data is not valid hex", i) + } + slot, err := strconv.ParseUint(p.SignedRequestAuth.Message.Slot, 10, 64) + if err != nil { + return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.message.slot is not a valid uint64", i) + } + sig, err := hexutil.Decode(p.SignedRequestAuth.Signature) + if err != nil { + return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.signature is not valid hex", i) + } + out := ð.BuilderPreferenceV1{ + Request: ð.BuilderPreferencesRequestV1{ + Preferences: ð.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(maxPayment)}, + Auth: ð.SignedRequestAuthV1{ + Message: ð.RequestAuthV1{Data: data, Slot: primitives.Slot(slot)}, + Signature: sig, + }, + }, + } + if p.MinBid != "" { + v, err := strconv.ParseUint(p.MinBid, 10, 64) + if err != nil { + return nil, errors.Errorf("builder_preferences[%d].min_bid is not a valid uint64", i) + } + g := primitives.Gwei(v) + out.MinBid = &g + } + if p.BuilderBoostFactor != "" { + v, err := strconv.ParseUint(p.BuilderBoostFactor, 10, 64) + if err != nil { + return nil, errors.Errorf("builder_preferences[%d].builder_boost_factor is not a valid uint64", i) + } + out.BuilderBoostFactor = &v + } + return out, nil +} diff --git a/beacon-chain/rpc/eth/validator/handlers_block_gloas.go b/beacon-chain/rpc/eth/validator/handlers_block_gloas.go index 1645a17e4d0d..d419e40b768d 100644 --- a/beacon-chain/rpc/eth/validator/handlers_block_gloas.go +++ b/beacon-chain/rpc/eth/validator/handlers_block_gloas.go @@ -88,6 +88,17 @@ func (s *Server) ProduceBlockV4(w http.ResponseWriter, r *http.Request) { graffiti = g } + // JIT (beacon-APIs #625): a POST carries the proposer's per-builder preferences inline. + var builderPreferences []*eth.BuilderPreferenceV1 + if r.Method == http.MethodPost { + prefs, err := parseBuilderPreferencesBody(r) + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusBadRequest) + return + } + builderPreferences = prefs + } + v1alpha1resp, err := s.V1Alpha1Server.GetBeaconBlock(ctx, ð.BlockRequest{ Slot: primitives.Slot(slot), RandaoReveal: randaoReveal, @@ -95,6 +106,7 @@ func (s *Server) ProduceBlockV4(w http.ResponseWriter, r *http.Request) { SkipMevBoost: false, BuilderBoostFactor: bbFactor, EagerPayloadStateRoot: includePayload, + BuilderPreferences: builderPreferences, }) if err != nil { httputil.HandleError(w, err.Error(), http.StatusInternalServerError) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel b/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel index 5aae83fb2413..29a35479ee50 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/BUILD.bazel @@ -222,6 +222,7 @@ go_test( "proposer_attestations_electra_test.go", "proposer_attestations_test.go", "proposer_bid_builder_test.go", + "proposer_bid_prefs_test.go", "proposer_bid_test.go", "proposer_bellatrix_test.go", "proposer_builder_test.go", diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go deleted file mode 100644 index 3590b6c6e86e..000000000000 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go +++ /dev/null @@ -1,32 +0,0 @@ -package validator - -import ( - "context" - - "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" - "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" - ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/protobuf/types/known/emptypb" -) - -// SubmitBuilderPreferences forwards a proposer's builder preferences to the configured builder. -func (vs *Server) SubmitBuilderPreferences(ctx context.Context, req *ethpb.SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { - ctx, span := trace.StartSpan(ctx, "ValidatorServer.SubmitBuilderPreferences") - defer span.End() - - if req == nil || req.Request == nil { - return nil, status.Error(codes.InvalidArgument, "builder preferences request is empty") - } - // Not gated on Configured(), gloas builders are dialed per URL from the request auth rather than the endpoint flag. - if vs.BlockBuilder == nil { - return nil, status.Error(codes.FailedPrecondition, "builder is not configured") - } - pubkey := bytesutil.ToBytes48(req.ValidatorPubkey) - if err := vs.BlockBuilder.SubmitBuilderPreferences(ctx, pubkey, req.Request); err != nil { - return nil, status.Errorf(codes.Internal, "could not submit builder preferences: %v", err) - } - vs.maxExecutionPayments.Store(pubkey, uint64(req.Request.Preferences.GetMaxExecutionPayment())) - return &emptypb.Empty{}, nil -} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go deleted file mode 100644 index 1fd490b27866..000000000000 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go +++ /dev/null @@ -1,61 +0,0 @@ -//go:build minimal - -package validator - -import ( - "testing" - - builderTest "github.com/OffchainLabs/prysm/v7/beacon-chain/builder/testing" - "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" - ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" - "github.com/OffchainLabs/prysm/v7/testing/require" - "github.com/pkg/errors" -) - -func TestServer_SubmitBuilderPreferences(t *testing.T) { - pubkey := bytesutil.ToBytes48([]byte{1, 2, 3}) - req := ðpb.SubmitBuilderPreferencesRequest{ - ValidatorPubkey: pubkey[:], - Request: ðpb.BuilderPreferencesRequestV1{ - Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: 1000}, - }, - } - - t.Run("stores max execution payment on success", func(t *testing.T) { - vs := &Server{BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true}} - _, err := vs.SubmitBuilderPreferences(t.Context(), req) - require.NoError(t, err) - v, ok := vs.maxExecutionPayments.Load(pubkey) - require.Equal(t, true, ok) - require.Equal(t, uint64(1000), v.(uint64)) - }) - - t.Run("nil request errors", func(t *testing.T) { - vs := &Server{BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true}} - _, err := vs.SubmitBuilderPreferences(t.Context(), ðpb.SubmitBuilderPreferencesRequest{ValidatorPubkey: pubkey[:]}) - require.ErrorContains(t, "request is empty", err) - }) - - t.Run("succeeds without the builder endpoint flag", func(t *testing.T) { - vs := &Server{BlockBuilder: &builderTest.MockBuilderService{HasConfigured: false}} - _, err := vs.SubmitBuilderPreferences(t.Context(), req) - require.NoError(t, err) - v, ok := vs.maxExecutionPayments.Load(pubkey) - require.Equal(t, true, ok) - require.Equal(t, uint64(1000), v.(uint64)) - }) - - t.Run("nil block builder errors", func(t *testing.T) { - vs := &Server{} - _, err := vs.SubmitBuilderPreferences(t.Context(), req) - require.ErrorContains(t, "builder is not configured", err) - }) - - t.Run("does not store when builder submission fails", func(t *testing.T) { - vs := &Server{BlockBuilder: &builderTest.MockBuilderService{HasConfigured: true, ErrSubmitBuilderPreferences: errors.New("boom")}} - _, err := vs.SubmitBuilderPreferences(t.Context(), req) - require.ErrorContains(t, "could not submit builder preferences", err) - _, ok := vs.maxExecutionPayments.Load(pubkey) - require.Equal(t, false, ok) - }) -} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index 4778885d3598..90995e7a0aed 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -111,7 +111,7 @@ func (vs *Server) GetBeaconBlock(ctx context.Context, req *ethpb.BlockRequest) ( builderBoostFactor = primitives.Gwei(req.BuilderBoostFactor.Value) } - resp, err := vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost, builderBoostFactor, full, req.EagerPayloadStateRoot, req.BuilderRequestAuths) + resp, err := vs.BuildBlockParallel(ctx, sBlk, head, req.SkipMevBoost, builderBoostFactor, full, req.EagerPayloadStateRoot, req.BuilderPreferences) l := log.WithFields(logrus.Fields{ "sinceSlotStartTime": time.Since(t), "validator": sBlk.Block().ProposerIndex(), @@ -191,9 +191,9 @@ func (vs *Server) getParentState(ctx context.Context, slot primitives.Slot) (sta return head, parentRoot, vs.ForkchoiceFetcher.FullBeatsEmpty(parentRoot), err } -func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool, builderBoostFactor primitives.Gwei, parentFull, eagerPayloadStateRoot bool, builderRequestAuths []*ethpb.SignedRequestAuthV1) (*ethpb.GenericBeaconBlock, error) { +func (vs *Server) BuildBlockParallel(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipMevBoost bool, builderBoostFactor primitives.Gwei, parentFull, eagerPayloadStateRoot bool, builderPreferences []*ethpb.BuilderPreferenceV1) (*ethpb.GenericBeaconBlock, error) { if sBlk.Version() >= version.Gloas { - return vs.buildBlockGloas(ctx, sBlk, head, skipMevBoost, parentFull, eagerPayloadStateRoot, builderRequestAuths) + return vs.buildBlockGloas(ctx, sBlk, head, skipMevBoost, parentFull, eagerPayloadStateRoot, builderPreferences) } return vs.buildBlockFulu(ctx, sBlk, head, skipMevBoost, builderBoostFactor, parentFull) } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index 62052bb44b61..11612c388ed1 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -3,6 +3,8 @@ package validator import ( "context" "fmt" + "math" + "math/big" "strings" "time" @@ -51,7 +53,7 @@ func (vs *Server) setExecutionPayloadBid( sBlk interfaces.SignedBeaconBlock, local *consensusblocks.GetPayloadResponse, builderBid *ethpb.SignedExecutionPayloadBid, - maxExecutionPayment uint64, + prefs bidPreferences, selfBuildOnly bool, ) (bidSource, error) { _, span := trace.StartSpan(ctx, "ProposerServer.setExecutionPayloadBid") @@ -63,7 +65,7 @@ func (vs *Server) setExecutionPayloadBid( if !selfBuildOnly { p2pBid := vs.cachedP2PBid(sBlk, local) - if bestBid, src := bestBid(local, p2pBid, builderBid, maxExecutionPayment); bestBid != nil { + if bestBid, src := bestBid(local, p2pBid, builderBid, prefs); bestBid != nil { if err := sBlk.SetSignedExecutionPayloadBid(bestBid); err != nil { return bidSourceSelfBuild, errors.Wrap(err, "could not set remote execution payload bid") } @@ -71,7 +73,7 @@ func (vs *Server) setExecutionPayloadBid( "slot": sBlk.Block().Slot(), "source": src, "builder": bestBid.Message.BuilderIndex, - "valueGwei": uint64(effectiveBidValue(bestBid, maxExecutionPayment)), + "valueGwei": uint64(effectiveBidValue(bestBid, prefs.maxPayment)), }).Info("Chose payload bid") return src, nil } @@ -100,22 +102,43 @@ func (vs *Server) setExecutionPayloadBid( return bidSourceSelfBuild, nil } -// Returns a nil bid when the local self-build wins. +// neutralBuilderBoostFactor is the 100% factor: builder bids compete on raw value. +// Callers must default a missing preference to this so an explicit 0 keeps its +// reserved "prefer local" meaning. +const neutralBuilderBoostFactor = 100 + +// bidPreferences carries the proposer's per-key builder bid-selection preferences. +type bidPreferences struct { + maxPayment uint64 + minBid uint64 + boostFactor uint64 +} + +// Returns a nil bid when the local self-build wins. A non-local bid must clear the +// min_bid floor and beat the local bid after the boost factor is applied. func bestBid( local *consensusblocks.GetPayloadResponse, p2pBid *ethpb.SignedExecutionPayloadBid, builderBid *ethpb.SignedExecutionPayloadBid, - maxExecutionPayment uint64, + prefs bidPreferences, ) (*ethpb.SignedExecutionPayloadBid, bidSource) { var bestBid *ethpb.SignedExecutionPayloadBid - bestValue := primitives.WeiToGwei(local.Bid) + var bestValue primitives.Gwei + localValue := primitives.WeiToGwei(local.Bid) src := bidSourceSelfBuild consider := func(bid *ethpb.SignedExecutionPayloadBid, from bidSource) { if bid == nil { return } - if value := effectiveBidValue(bid, maxExecutionPayment); value > bestValue { + value := effectiveBidValue(bid, prefs.maxPayment) + if uint64(value) < prefs.minBid { + return + } + if !builderBeatsLocal(value, localValue, prefs.boostFactor) { + return + } + if bestBid == nil || value > bestValue { bestBid, bestValue, src = bid, value, from } } @@ -126,6 +149,20 @@ func bestBid( return bestBid, src } +// builderBeatsLocal reports whether a boosted non-local bid beats the local bid. +// Reserved factors: 0 always prefers local, max always prefers the builder. +func builderBeatsLocal(builderValue, localValue primitives.Gwei, boostFactor uint64) bool { + switch boostFactor { + case 0: + return false + case math.MaxUint64: + return true + } + lhs := new(big.Int).Mul(new(big.Int).SetUint64(uint64(builderValue)), new(big.Int).SetUint64(boostFactor)) + rhs := new(big.Int).Mul(new(big.Int).SetUint64(uint64(localValue)), big.NewInt(100)) + return lhs.Cmp(rhs) > 0 +} + // The proposer's total take, the execution payment counts only up to the proposer's max preference. func effectiveBidValue(bid *ethpb.SignedExecutionPayloadBid, maxExecutionPayment uint64) primitives.Gwei { payment := bid.Message.ExecutionPayment diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go index 396bf4a8d65c..b1821d577a5e 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go @@ -122,7 +122,7 @@ func TestBestBid(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, src := bestBid(tt.local, tt.p2p, tt.builder, tt.maxPayment) + got, src := bestBid(tt.local, tt.p2p, tt.builder, bidPreferences{maxPayment: tt.maxPayment, boostFactor: 100}) require.Equal(t, tt.wantSrc, src) if tt.wantNil { require.IsNil(t, got) @@ -387,7 +387,7 @@ func TestSetExecutionPayloadBid_PrefersBuilderBid(t *testing.T) { // No P2P cache, so the Builder-API bid is the only remote candidate. vs := &Server{} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, builderBid, 1000, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, builderBid, bidPreferences{maxPayment: 1000, boostFactor: 100}, false) require.NoError(t, err) require.Equal(t, bidSourceBuilderAPI, src) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go new file mode 100644 index 000000000000..fcb52c70bec6 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go @@ -0,0 +1,117 @@ +//go:build minimal + +package validator + +import ( + "math" + "testing" + + consensusblocks "github.com/OffchainLabs/prysm/v7/consensus-types/blocks" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/testing/require" +) + +// TestBestBid_BoostFactor exercises the boost-factor and min-bid preferences that +// gate whether a builder bid can beat the local self-build. +func TestBestBid_BoostFactor(t *testing.T) { + const builderIdx = primitives.BuilderIndex(2) + tests := []struct { + name string + local *consensusblocks.GetPayloadResponse + builder *ethpb.SignedExecutionPayloadBid + prefs bidPreferences + wantSrc bidSource + wantNil bool + }{ + { + name: "neutral 100 higher builder wins", + local: localWithGwei(100), + builder: newBid(1000, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: 100}, + wantSrc: bidSourceBuilderAPI, + }, + { + name: "neutral 100 lower builder loses", + local: localWithGwei(1000), + builder: newBid(100, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: 100}, + wantSrc: bidSourceSelfBuild, + wantNil: true, + }, + { + name: "boost 0 prefers local even when builder is higher", + local: localWithGwei(0), + builder: newBid(1000, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: 0}, + wantSrc: bidSourceSelfBuild, + wantNil: true, + }, + { + name: "boost max prefers builder even when local is higher", + local: localWithGwei(1000), + builder: newBid(1, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: math.MaxUint64}, + wantSrc: bidSourceBuilderAPI, + }, + { + name: "boost 200 lets 60 beat local 100", + local: localWithGwei(100), + builder: newBid(60, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: 200}, + wantSrc: bidSourceBuilderAPI, + }, + { + name: "min_bid rejects builder below floor", + local: localWithGwei(0), + builder: newBid(50, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: 100, minBid: 100}, + wantSrc: bidSourceSelfBuild, + wantNil: true, + }, + { + name: "min_bid allows builder at floor", + local: localWithGwei(0), + builder: newBid(100, 0, builderIdx), + prefs: bidPreferences{maxPayment: 1000, boostFactor: 100, minBid: 100}, + wantSrc: bidSourceBuilderAPI, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, src := bestBid(tt.local, nil, tt.builder, tt.prefs) + require.Equal(t, tt.wantSrc, src) + if tt.wantNil { + require.IsNil(t, got) + return + } + require.NotNil(t, got) + require.Equal(t, builderIdx, got.Message.BuilderIndex) + }) + } +} + +func TestBuilderBeatsLocal(t *testing.T) { + tests := []struct { + name string + builderValue primitives.Gwei + localValue primitives.Gwei + boostFactor uint64 + want bool + }{ + {"neutral higher wins", 200, 100, 100, true}, + {"neutral equal loses", 100, 100, 100, false}, + {"neutral lower loses", 50, 100, 100, false}, + {"boost 0 always local", 1000, 0, 0, false}, + {"boost max always builder", 1, 1000, math.MaxUint64, true}, + {"boost 200 lifts 60 over 100", 60, 100, 200, true}, + {"boost 200 leaves 40 under 100", 40, 100, 200, false}, + // Large values must not overflow uint64 math (bestBid uses big.Int internally). + {"no overflow on large values", primitives.Gwei(1) << 40, primitives.Gwei(1) << 40, 200, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + require.Equal(t, tt.want, builderBeatsLocal(tt.builderValue, tt.localValue, tt.boostFactor)) + }) + } +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go index bb9db8628b6b..3802c5fe4463 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go @@ -56,7 +56,7 @@ func TestSetSelfBuildExecutionPayloadBid(t *testing.T) { vs := &Server{} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) @@ -125,7 +125,7 @@ func TestSetSelfBuildExecutionPayloadBid_BlobCommitments(t *testing.T) { } vs := &Server{} - _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, true) + _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, true) require.NoError(t, err) signedBid, err := sBlk.Block().Body().SignedExecutionPayloadBid() @@ -149,10 +149,10 @@ func TestSetSelfBuildExecutionPayloadBid_NilPayload(t *testing.T) { vs := &Server{} - _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, nil, nil, 0, false) + _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, nil, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) require.ErrorContains(t, "local execution payload is nil", err) - _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, &consensusblocks.GetPayloadResponse{}, nil, 0, false) + _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, &consensusblocks.GetPayloadResponse{}, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) require.ErrorContains(t, "local execution payload is nil", err) } @@ -215,7 +215,7 @@ func TestSetExecutionPayloadBid_PrefersP2PBid(t *testing.T) { vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) require.NoError(t, err) require.Equal(t, bidSourceP2P, src) @@ -289,7 +289,7 @@ func TestSetExecutionPayloadBid_PrefersLocalWhenHigherValue(t *testing.T) { vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) @@ -362,7 +362,7 @@ func TestSetExecutionPayloadBid_SelfBuildOnlyIgnoresCache(t *testing.T) { vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, true) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, true) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) @@ -414,7 +414,7 @@ func TestSetExecutionPayloadBid_FallsBackToSelfBuildWhenNoCachedBid(t *testing.T bidCache := cache.NewHighestExecutionPayloadBidCache() vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go index 2921c6666a92..56ab468b996e 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go @@ -15,7 +15,7 @@ import ( // buildBlockGloas builds a Gloas (ePBS) block, whose body carries an execution payload bid // rather than the payload itself. The payload is revealed separately via the envelope. -func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipBuilder, parentFull, eagerPayloadStateRoot bool, builderRequestAuths []*ethpb.SignedRequestAuthV1) (*ethpb.GenericBeaconBlock, error) { +func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBeaconBlock, head state.BeaconState, skipBuilder, parentFull, eagerPayloadStateRoot bool, builderPreferences []*ethpb.BuilderPreferenceV1) (*ethpb.GenericBeaconBlock, error) { if parentFull { if err := vs.applyParentExecutionPayloadToHead(ctx, head, sBlk.Block().ParentRoot()); err != nil { return nil, status.Errorf(codes.Internal, "Could not apply parent execution payload: %v", err) @@ -45,8 +45,8 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea selfBuildOnly := local.OverrideBuilder || skipBuilder var builderBid *ethpb.SignedExecutionPayloadBid var builderURL string - var maxExecutionPayment uint64 - if !selfBuildOnly && len(builderRequestAuths) > 0 { + prefs := bidPreferences{boostFactor: neutralBuilderBoostFactor} + if !selfBuildOnly && len(builderPreferences) > 0 { val, valErr := head.ValidatorAtIndexReadOnly(sBlk.Block().ProposerIndex()) parentGasLimit, glErr := vs.ForkchoiceFetcher.GasLimit(sBlk.Block().ParentRoot()) switch { @@ -56,9 +56,8 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea log.WithError(glErr).Error("Could not get parent gas limit for builder bid request") default: pubkey := val.PublicKey() - if v, ok := vs.maxExecutionPayments.Load(pubkey); ok { - maxExecutionPayment, _ = v.(uint64) - } + var auths []*ethpb.SignedRequestAuthV1 + auths, prefs = builderAuthsAndPrefs(builderPreferences) pref := vs.proposerPreferenceForProposal(ctx, head, sBlk.Block().Slot(), sBlk.Block().ProposerIndex()) feeRecipient := pref.FeeRecipientOrDefault() builderBid, builderURL = vs.getBuilderExecutionPayloadBid(ctx, head, &builderBidQuery{ @@ -66,15 +65,15 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea parentRoot: sBlk.Block().ParentRoot(), parentHash: bytesutil.ToBytes32(local.ExecutionData.ParentHash()), pubkey: pubkey, - maxPayment: maxExecutionPayment, + maxPayment: prefs.maxPayment, feeRecipient: feeRecipient[:], parentGasLimit: parentGasLimit, targetGasLimit: pref.GasLimitOr(parentGasLimit), - auths: builderRequestAuths, + auths: auths, }) } } - src, bidErr := vs.setExecutionPayloadBid(ctx, sBlk, local, builderBid, maxExecutionPayment, selfBuildOnly) + src, bidErr := vs.setExecutionPayloadBid(ctx, sBlk, local, builderBid, prefs, selfBuildOnly) if bidErr != nil { return nil, status.Errorf(codes.Internal, "Could not set execution payload bid: %v", bidErr) } @@ -119,3 +118,31 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea } return blk, nil } + +// builderAuthsAndPrefs extracts the signed request auths and the effective +// proposer-local bid preferences from the inline builder preferences carried on +// the block request. The effective preferences use the first valid entry as the +// proposal baseline. The beacon node enforces the caps locally during bid +// selection; communicating preferences to the builder is left to the bid request. +func builderAuthsAndPrefs(prefs []*ethpb.BuilderPreferenceV1) ([]*ethpb.SignedRequestAuthV1, bidPreferences) { + auths := make([]*ethpb.SignedRequestAuthV1, 0, len(prefs)) + bp := bidPreferences{boostFactor: neutralBuilderBoostFactor} + baselineSet := false + for _, p := range prefs { + if p == nil || p.Request == nil || p.Request.Auth == nil { + continue + } + auths = append(auths, p.Request.Auth) + if !baselineSet { + bp.maxPayment = uint64(p.Request.Preferences.GetMaxExecutionPayment()) + if p.MinBid != nil { + bp.minBid = uint64(p.GetMinBid()) + } + if p.BuilderBoostFactor != nil { + bp.boostFactor = p.GetBuilderBoostFactor() + } + baselineSet = true + } + } + return auths, bp +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/server.go b/beacon-chain/rpc/prysm/v1alpha1/validator/server.go index 353ee14f3ecd..0018fa61b9f2 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/server.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/server.go @@ -97,7 +97,6 @@ type Server struct { lastBidSlot primitives.Slot lastBidSource bidSource // Guarded by lastBidLock, set during Gloas block build, read when proposing. lastBidBuilderURL string // Guarded by lastBidLock, winning Builder-API URL for lastBidSlot. - maxExecutionPayments sync.Map // validator pubkey [48]byte -> max execution payment (Gwei uint64). } // Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API. diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md new file mode 100644 index 000000000000..068da6bea866 --- /dev/null +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -0,0 +1,8 @@ +### Added + +- Add `GET`/`POST /eth/v1/validator/config` keymanager endpoints (keymanager-APIs #87), managing fee recipient, target gas limit, graffiti, and per-key builder preferences (builder list, `min_bid`, `max_execution_payment`, `builder_boost_factor`, proxy) as one atomic per-key document. POST replaces a key's fragment, an empty document clears it, and each key is applied independently with a `set`/`not_found`/`error` status. +- Wire per-key `builder_boost_factor` and `min_bid` into Gloas builder-API bid selection, and `builder_boost_factor` into the pre-Gloas `builder_boost_factor` block-production query. + +### Changed + +- Proposer builder config `enabled` is now presence-tracked so an unset value inherits from `default_config` instead of defaulting to disabled. diff --git a/cmd/prysmctl/validator/proposer_settings.go b/cmd/prysmctl/validator/proposer_settings.go index 0a97481eb9a5..8498f9f8c847 100644 --- a/cmd/prysmctl/validator/proposer_settings.go +++ b/cmd/prysmctl/validator/proposer_settings.go @@ -69,8 +69,9 @@ func getProposerSettings(c *cli.Context, r io.Reader) error { log.Infof("The default fee recipient is set to %s", defaultFeeRecipient) var builderSettings *validatorpb.BuilderConfig if c.Bool(WithBuilderFlag.Name) { + enabled := true builderSettings = &validatorpb.BuilderConfig{ - Enabled: true, + Enabled: &enabled, GasLimit: validatorType.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), } } else { diff --git a/config/proposer/BUILD.bazel b/config/proposer/BUILD.bazel index 007baab9d477..093fb80ed8cb 100644 --- a/config/proposer/BUILD.bazel +++ b/config/proposer/BUILD.bazel @@ -36,5 +36,6 @@ go_test( "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/config/proposer/loader/BUILD.bazel b/config/proposer/loader/BUILD.bazel index 401a0fbcb33c..53f04c5029e9 100644 --- a/config/proposer/loader/BUILD.bazel +++ b/config/proposer/loader/BUILD.bazel @@ -22,6 +22,7 @@ go_test( "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@com_github_urfave_cli_v2//:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/config/proposer/loader/loader.go b/config/proposer/loader/loader.go index 27ee4ef7a271..b6604e0f9da7 100644 --- a/config/proposer/loader/loader.go +++ b/config/proposer/loader/loader.go @@ -46,8 +46,9 @@ type SettingsLoaderOption func(cliCtx *cli.Context, psl *SettingsLoader) error func WithBuilderConfig() SettingsLoaderOption { return func(cliCtx *cli.Context, psl *SettingsLoader) error { if cliCtx.Bool(flags.EnableBuilderFlag.Name) { + enabled := true psl.options.builderConfig = &proposer.BuilderConfig{ - Enabled: true, + Enabled: &enabled, GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), } } @@ -331,7 +332,7 @@ func mergeProposerSettingsV1(merged, loaded, db *validatorpb.ProposerSettingsPay merged.DefaultConfig = &validatorpb.ProposerOptionPayload{Builder: builderConfig} case gasLimitOnly != nil: merged.DefaultConfig = &validatorpb.ProposerOptionPayload{ - Builder: &validatorpb.BuilderConfig{Enabled: false, GasLimit: *gasLimitOnly}, + Builder: &validatorpb.BuilderConfig{GasLimit: *gasLimitOnly}, } } } @@ -383,7 +384,7 @@ func processBuilderConfig(current *validatorpb.BuilderConfig, override *validato return override } if gasLimitOnly != nil { - return &validatorpb.BuilderConfig{Enabled: false, GasLimit: *gasLimitOnly} + return &validatorpb.BuilderConfig{GasLimit: *gasLimitOnly} } return nil } diff --git a/config/proposer/loader/loader_test.go b/config/proposer/loader/loader_test.go index 3875358425bb..773b8b96c67b 100644 --- a/config/proposer/loader/loader_test.go +++ b/config/proposer/loader/loader_test.go @@ -8,6 +8,12 @@ import ( "os" "testing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + logtest "github.com/sirupsen/logrus/hooks/test" + "github.com/urfave/cli/v2" + "google.golang.org/protobuf/proto" + "github.com/OffchainLabs/prysm/v7/cmd/validator/flags" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" @@ -19,10 +25,6 @@ import ( "github.com/OffchainLabs/prysm/v7/testing/require" "github.com/OffchainLabs/prysm/v7/validator/db/iface" dbTest "github.com/OffchainLabs/prysm/v7/validator/db/testing" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - logtest "github.com/sirupsen/logrus/hooks/test" - "github.com/urfave/cli/v2" ) func TestProposerSettingsLoader(t *testing.T) { @@ -108,7 +110,7 @@ func TestProposerSettingsLoader(t *testing.T) { Graffiti: "some graffiti", }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(30000000), }, }, @@ -118,7 +120,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -159,7 +161,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -186,7 +188,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -196,7 +198,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -212,7 +214,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -253,7 +255,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0xae967917c465db8578ca9024c205720b1a3651A9"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -311,7 +313,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -320,7 +322,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x60155530FCE8a85ec7055A5F8b2bE214B3DaeFd4"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(35000000), }, }, @@ -330,7 +332,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -386,7 +388,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 40000000, }, }, @@ -396,7 +398,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -442,7 +444,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -469,7 +471,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 50000000, }, }, @@ -498,7 +500,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 50000000, }, }, @@ -508,7 +510,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: validator.Uint64(50000000), }, }, @@ -564,7 +566,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -574,7 +576,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -602,7 +604,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -612,7 +614,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -639,7 +641,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -649,7 +651,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -670,7 +672,7 @@ func TestProposerSettingsLoader(t *testing.T) { return &proposer.Settings{ DefaultConfig: &proposer.Option{ BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -716,7 +718,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -726,7 +728,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -753,7 +755,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -763,7 +765,7 @@ func TestProposerSettingsLoader(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -993,7 +995,7 @@ func Test_ProposerSettingsLoaderWithOnlyBuilder_DoesNotSaveInDB(t *testing.T) { want := &proposer.Settings{ DefaultConfig: &proposer.Option{ BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), Relays: nil, }, @@ -1027,7 +1029,7 @@ func Test_ProposerSettingsLoader_GasLimitWithoutBuilder(t *testing.T) { require.NotNil(t, got) require.NotNil(t, got.DefaultConfig) require.NotNil(t, got.DefaultConfig.BuilderConfig) - require.Equal(t, false, got.DefaultConfig.BuilderConfig.Enabled) + require.Equal(t, false, got.DefaultConfig.BuilderConfig.IsEnabled()) require.Equal(t, validator.Uint64(12345678), got.DefaultConfig.BuilderConfig.GasLimit) }) } @@ -1103,7 +1105,7 @@ func Test_ProposerSettingsLoader_DoesNotMigrateAtLoad(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: validator.Uint64(99000000), }, }, @@ -1184,7 +1186,7 @@ func Test_mergeProposerSettings_VersionPrecedence(t *testing.T) { merged := mergeProposerSettings( &validatorpb.ProposerSettingsPayload{ DefaultConfig: &validatorpb.ProposerOptionPayload{ - Builder: &validatorpb.BuilderConfig{Enabled: true, GasLimit: 30000000}, + Builder: &validatorpb.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 30000000}, }, }, &validatorpb.ProposerSettingsPayload{Version: proposer.SchemaV2}, @@ -1243,7 +1245,7 @@ func Test_mergeProposerSettings_CreatesDefaultFromGasLimitFlag(t *testing.T) { ) require.NotNil(t, merged.DefaultConfig) require.NotNil(t, merged.DefaultConfig.Builder) - require.Equal(t, false, merged.DefaultConfig.Builder.Enabled) + require.Equal(t, false, merged.DefaultConfig.Builder.GetEnabled()) require.Equal(t, gl, merged.DefaultConfig.Builder.GasLimit) } @@ -1261,7 +1263,7 @@ func Test_mergeProposerSettings_V2GasLimitOnlyGoesToOption(t *testing.T) { func Test_mergeProposerSettings_VersionGatesBuilderReset(t *testing.T) { v1Builder := func() *validatorpb.BuilderConfig { - return &validatorpb.BuilderConfig{Enabled: true, GasLimit: 40000000, Relays: []string{"r"}} + return &validatorpb.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 40000000, Relays: []string{"r"}} } t.Run("v1 db without enable-builder drops DB builder", func(t *testing.T) { db := &validatorpb.ProposerSettingsPayload{ diff --git a/config/proposer/settings.go b/config/proposer/settings.go index 0679a27e8e0e..9a49ed161020 100644 --- a/config/proposer/settings.go +++ b/config/proposer/settings.go @@ -82,10 +82,32 @@ func verifyOption(key string, option *validatorpb.ProposerOptionPayload) error { // BuilderConfig is the struct representation of the JSON config file set in the validator through the CLI. // GasLimit is a number set to help the network decide on the maximum gas in each block. type BuilderConfig struct { - Enabled bool `json:"enabled" yaml:"enabled"` - GasLimit validator.Uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"` - Relays []string `json:"relays,omitempty" yaml:"relays,omitempty"` - MaxExecutionPayment validator.Uint64 `json:"max_execution_payment,omitempty" yaml:"max_execution_payment,omitempty"` + Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` + GasLimit validator.Uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"` + Relays []string `json:"relays,omitempty" yaml:"relays,omitempty"` + MaxExecutionPayment validator.Uint64 `json:"max_execution_payment,omitempty" yaml:"max_execution_payment,omitempty"` + Builders []*BuilderEntry `json:"builders,omitempty" yaml:"builders,omitempty"` + Proxy *string `json:"proxy,omitempty" yaml:"proxy,omitempty"` + MinBid *validator.Uint64 `json:"min_bid,omitempty" yaml:"min_bid,omitempty"` + BuilderBoostFactor *validator.Uint64 `json:"builder_boost_factor,omitempty" yaml:"builder_boost_factor,omitempty"` +} + +// BuilderEntry is one builder in a proposer's per-key builder list. Unset fields +// fall back to the enclosing BuilderConfig, then default_config. +type BuilderEntry struct { + URL string `json:"url" yaml:"url"` + Pubkey []byte `json:"pubkey,omitempty" yaml:"pubkey,omitempty"` + AuthData []byte `json:"auth_data,omitempty" yaml:"auth_data,omitempty"` + Proxy *string `json:"proxy,omitempty" yaml:"proxy,omitempty"` + MinBid *validator.Uint64 `json:"min_bid,omitempty" yaml:"min_bid,omitempty"` + MaxExecutionPayment *validator.Uint64 `json:"max_execution_payment,omitempty" yaml:"max_execution_payment,omitempty"` + BuilderBoostFactor *validator.Uint64 `json:"builder_boost_factor,omitempty" yaml:"builder_boost_factor,omitempty"` +} + +// IsEnabled reports whether the builder path is explicitly enabled. A nil config +// or unset enabled field is treated as disabled. +func (bc *BuilderConfig) IsEnabled() bool { + return bc != nil && bc.Enabled != nil && *bc.Enabled } // BuilderConfigFromConsensus converts protobuf to a builder config used in in-memory storage @@ -97,15 +119,44 @@ func BuilderConfigFromConsensus(from *validatorpb.BuilderConfig) *BuilderConfig Enabled: from.Enabled, GasLimit: from.GasLimit, MaxExecutionPayment: from.MaxExecutionPayment, + Proxy: from.Proxy, + MinBid: from.MinBid, + BuilderBoostFactor: from.BuilderBoostFactor, } if from.Relays != nil { relays := make([]string, len(from.Relays)) copy(relays, from.Relays) c.Relays = relays } + if len(from.Builders) > 0 { + c.Builders = make([]*BuilderEntry, 0, len(from.Builders)) + for _, b := range from.Builders { + c.Builders = append(c.Builders, builderEntryFromConsensus(b)) + } + } return c } +func builderEntryFromConsensus(from *validatorpb.BuilderEntry) *BuilderEntry { + if from == nil { + return nil + } + e := &BuilderEntry{ + URL: from.Url, + Proxy: from.Proxy, + MinBid: from.MinBid, + MaxExecutionPayment: from.MaxExecutionPayment, + BuilderBoostFactor: from.BuilderBoostFactor, + } + if from.Pubkey != nil { + e.Pubkey = bytesutil.SafeCopyBytes(from.Pubkey) + } + if from.AuthData != nil { + e.AuthData = bytesutil.SafeCopyBytes(from.AuthData) + } + return e +} + // Schema versions for proposer settings. SchemaV1Unset is the proto3 zero // value — every existing v1 user has it, since the version field is new. // Both SchemaV1Unset and SchemaV1 are legacy v1 inputs to the migration. @@ -239,18 +290,66 @@ func (bc *BuilderConfig) Clone() *BuilderConfig { return nil } c := &BuilderConfig{} - c.Enabled = bc.Enabled + c.Enabled = cloneBool(bc.Enabled) c.GasLimit = bc.GasLimit c.MaxExecutionPayment = bc.MaxExecutionPayment - var relays []string + c.Proxy = cloneString(bc.Proxy) + c.MinBid = cloneUint64(bc.MinBid) + c.BuilderBoostFactor = cloneUint64(bc.BuilderBoostFactor) if bc.Relays != nil { - relays = make([]string, len(bc.Relays)) + relays := make([]string, len(bc.Relays)) copy(relays, bc.Relays) c.Relays = relays } + if len(bc.Builders) > 0 { + c.Builders = make([]*BuilderEntry, 0, len(bc.Builders)) + for _, b := range bc.Builders { + c.Builders = append(c.Builders, b.Clone()) + } + } return c } +// Clone creates a deep copy of a builder entry +func (be *BuilderEntry) Clone() *BuilderEntry { + if be == nil { + return nil + } + return &BuilderEntry{ + URL: be.URL, + Pubkey: bytesutil.SafeCopyBytes(be.Pubkey), + AuthData: bytesutil.SafeCopyBytes(be.AuthData), + Proxy: cloneString(be.Proxy), + MinBid: cloneUint64(be.MinBid), + MaxExecutionPayment: cloneUint64(be.MaxExecutionPayment), + BuilderBoostFactor: cloneUint64(be.BuilderBoostFactor), + } +} + +func cloneBool(v *bool) *bool { + if v == nil { + return nil + } + c := *v + return &c +} + +func cloneString(v *string) *string { + if v == nil { + return nil + } + c := *v + return &c +} + +func cloneUint64(v *validator.Uint64) *validator.Uint64 { + if v == nil { + return nil + } + c := *v + return &c +} + // Clone creates a deep copy of graffiti config func (gc *GraffitiConfig) Clone() *GraffitiConfig { if gc == nil { @@ -265,18 +364,41 @@ func (bc *BuilderConfig) ToConsensus() *validatorpb.BuilderConfig { return nil } c := &validatorpb.BuilderConfig{} - c.Enabled = bc.Enabled - var relays []string + c.Enabled = cloneBool(bc.Enabled) if bc.Relays != nil { - relays = make([]string, len(bc.Relays)) + relays := make([]string, len(bc.Relays)) copy(relays, bc.Relays) c.Relays = relays } c.GasLimit = bc.GasLimit c.MaxExecutionPayment = bc.MaxExecutionPayment + c.Proxy = cloneString(bc.Proxy) + c.MinBid = cloneUint64(bc.MinBid) + c.BuilderBoostFactor = cloneUint64(bc.BuilderBoostFactor) + if len(bc.Builders) > 0 { + c.Builders = make([]*validatorpb.BuilderEntry, 0, len(bc.Builders)) + for _, b := range bc.Builders { + c.Builders = append(c.Builders, b.toConsensus()) + } + } return c } +func (be *BuilderEntry) toConsensus() *validatorpb.BuilderEntry { + if be == nil { + return nil + } + return &validatorpb.BuilderEntry{ + Url: be.URL, + Pubkey: bytesutil.SafeCopyBytes(be.Pubkey), + AuthData: bytesutil.SafeCopyBytes(be.AuthData), + Proxy: cloneString(be.Proxy), + MinBid: cloneUint64(be.MinBid), + MaxExecutionPayment: cloneUint64(be.MaxExecutionPayment), + BuilderBoostFactor: cloneUint64(be.BuilderBoostFactor), + } +} + func (ps *Settings) isV2() bool { return ps != nil && ps.Version == SchemaV2 } @@ -372,7 +494,7 @@ func (ps *Settings) SetGasLimit(pubkey [fieldparams.BLSPubkeyLength]byte, gasLim return nil } builderEnabled := func(o *Option) bool { - return o != nil && o.BuilderConfig != nil && o.BuilderConfig.Enabled + return o != nil && o.BuilderConfig.IsEnabled() } if ps.ProposeConfig == nil { if !builderEnabled(ps.DefaultConfig) { diff --git a/config/proposer/settings_test.go b/config/proposer/settings_test.go index 0e1d912a25a1..bc6f71b65877 100644 --- a/config/proposer/settings_test.go +++ b/config/proposer/settings_test.go @@ -3,15 +3,17 @@ package proposer import ( "testing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + logtest "github.com/sirupsen/logrus/hooks/test" + "google.golang.org/protobuf/proto" + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/consensus-types/validator" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" "github.com/OffchainLabs/prysm/v7/testing/assert" "github.com/OffchainLabs/prysm/v7/testing/require" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - logtest "github.com/sirupsen/logrus/hooks/test" ) func Test_Proposer_Setting_Cloning(t *testing.T) { @@ -25,7 +27,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, MaxExecutionPayment: validator.Uint64(1000000000), @@ -37,7 +39,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), Relays: []string{"https://example-relay.com"}, MaxExecutionPayment: validator.Uint64(2000000000), @@ -67,7 +69,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { clone := settings.DefaultConfig.BuilderConfig.Clone() config := BuilderConfigFromConsensus(clone.ToConsensus()) require.DeepEqual(t, config.Relays, clone.Relays) - require.Equal(t, config.Enabled, clone.Enabled) + require.DeepEqual(t, config.Enabled, clone.Enabled) require.Equal(t, config.GasLimit, clone.GasLimit) require.Equal(t, config.MaxExecutionPayment, clone.MaxExecutionPayment) }) @@ -80,7 +82,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { require.Equal(t, true, pok) require.Equal(t, option.FeeRecipientConfig.FeeRecipient.Hex(), potion.FeeRecipient) require.Equal(t, settings.DefaultConfig.FeeRecipientConfig.FeeRecipient.Hex(), payload.DefaultConfig.FeeRecipient) - require.Equal(t, settings.DefaultConfig.BuilderConfig.Enabled, payload.DefaultConfig.Builder.Enabled) + require.DeepEqual(t, settings.DefaultConfig.BuilderConfig.Enabled, payload.DefaultConfig.Builder.Enabled) potion.FeeRecipient = fee newSettings, err := SettingFromConsensus(payload) require.NoError(t, err) @@ -114,7 +116,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, }, @@ -133,7 +135,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, }, @@ -150,7 +152,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, }, @@ -161,7 +163,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, }, @@ -194,7 +196,7 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { ProposeConfig: nil, DefaultConfig: &Option{ BuilderConfig: &BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, }, @@ -280,7 +282,7 @@ func TestSettings_GasLimit(t *testing.T) { t.Run("v1 per-validator zero BuilderConfig.GasLimit falls back to default", func(t *testing.T) { ps := &Settings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{ - pk: {BuilderConfig: &BuilderConfig{Enabled: true}}, + pk: {BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true)}}, }, DefaultConfig: &Option{BuilderConfig: &BuilderConfig{GasLimit: validator.Uint64(60_000_000)}}, } @@ -289,7 +291,7 @@ func TestSettings_GasLimit(t *testing.T) { t.Run("v1 zero default BuilderConfig.GasLimit falls back to chain default", func(t *testing.T) { ps := &Settings{ - DefaultConfig: &Option{BuilderConfig: &BuilderConfig{Enabled: true}}, + DefaultConfig: &Option{BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true)}}, } require.Equal(t, chainDefault, ps.GasLimit(pk)) }) @@ -336,7 +338,7 @@ func TestSettings_SetGasLimit(t *testing.T) { t.Run("v1 with disabled builder rejects", func(t *testing.T) { ps := &Settings{ - DefaultConfig: &Option{BuilderConfig: &BuilderConfig{Enabled: false}}, + DefaultConfig: &Option{BuilderConfig: &BuilderConfig{Enabled: proto.Bool(false)}}, } err := ps.SetGasLimit(pk, validator.Uint64(80_000_000)) require.ErrorContains(t, "Gas limit changes only apply when builder is enabled", err) @@ -347,7 +349,7 @@ func TestSettings_SetGasLimit(t *testing.T) { ps := &Settings{ DefaultConfig: &Option{ FeeRecipientConfig: &FeeRecipientConfig{FeeRecipient: feeRecipient}, - BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(30_000_000)}, + BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(30_000_000)}, }, } require.NoError(t, ps.SetGasLimit(pk, validator.Uint64(90_000_000))) @@ -359,7 +361,7 @@ func TestSettings_SetGasLimit(t *testing.T) { t.Run("v1 updates existing enabled-builder per-validator entry", func(t *testing.T) { ps := &Settings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{ - pk: {BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(10_000_000)}}, + pk: {BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(10_000_000)}}, }, } require.NoError(t, ps.SetGasLimit(pk, validator.Uint64(20_000_000))) @@ -369,7 +371,7 @@ func TestSettings_SetGasLimit(t *testing.T) { t.Run("v1 per-validator entry with disabled builder rejects", func(t *testing.T) { ps := &Settings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{ - pk: {BuilderConfig: &BuilderConfig{Enabled: false}}, + pk: {BuilderConfig: &BuilderConfig{Enabled: proto.Bool(false)}}, }, } err := ps.SetGasLimit(pk, validator.Uint64(20_000_000)) @@ -456,7 +458,7 @@ func TestSettings_WarnDeprecatedSchema(t *testing.T) { v1Settings := &Settings{ Version: SchemaV1, DefaultConfig: &Option{ - BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: 30000000}, + BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: 30000000}, }, } @@ -507,7 +509,7 @@ func TestSettings_UpgradeToV2(t *testing.T) { t.Run("v1 default lifts BuilderConfig.GasLimit to top-level and retains builder relays", func(t *testing.T) { ps := &Settings{ DefaultConfig: &Option{ - BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(42_000_000), Relays: []string{"http://b:8080"}}, + BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(42_000_000), Relays: []string{"http://b:8080"}}, }, } require.Equal(t, true, ps.UpgradeToV2()) @@ -536,8 +538,8 @@ func TestSettings_UpgradeToV2(t *testing.T) { pk2 := bytesutil.ToBytes48(pubkey2) ps := &Settings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{ - pk: {BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(35_000_000)}}, - pk2: {GasLimit: validator.Uint64(50_000_000), BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(40_000_000)}}, + pk: {BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(35_000_000)}}, + pk2: {GasLimit: validator.Uint64(50_000_000), BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(40_000_000)}}, }, } require.Equal(t, true, ps.UpgradeToV2()) @@ -554,7 +556,7 @@ func TestSettings_UpgradeToV2(t *testing.T) { ps := &Settings{ Version: SchemaV2, DefaultConfig: &Option{ - BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(42_000_000)}, + BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(42_000_000)}, }, } require.Equal(t, false, ps.UpgradeToV2()) @@ -607,9 +609,9 @@ func TestSettings_TargetGasLimit(t *testing.T) { t.Run("builder gas limits are never consulted", func(t *testing.T) { ps := &Settings{ ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{ - pk: {BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(35_000_000)}}, + pk: {BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(35_000_000)}}, }, - DefaultConfig: &Option{BuilderConfig: &BuilderConfig{Enabled: true, GasLimit: validator.Uint64(40_000_000)}}, + DefaultConfig: &Option{BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(40_000_000)}}, } require.Equal(t, chainDefault, ps.TargetGasLimit(pk)) }) diff --git a/proto/prysm/v1alpha1/gloas_builder_api.pb.go b/proto/prysm/v1alpha1/gloas_builder_api.pb.go index 3f9044a0c843..bfb3a8cdcd7a 100755 --- a/proto/prysm/v1alpha1/gloas_builder_api.pb.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.pb.go @@ -223,28 +223,29 @@ func (x *BuilderPreferencesRequestV1) GetAuth() *SignedRequestAuthV1 { return nil } -type SubmitBuilderPreferencesRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - ValidatorPubkey []byte `protobuf:"bytes,1,opt,name=validator_pubkey,json=validatorPubkey,proto3" json:"validator_pubkey,omitempty"` - Request *BuilderPreferencesRequestV1 `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache +type BuilderPreferenceV1 struct { + state protoimpl.MessageState `protogen:"open.v1"` + Request *BuilderPreferencesRequestV1 `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` + MinBid *github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei `protobuf:"varint,2,opt,name=min_bid,json=minBid,proto3,oneof" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei"` + BuilderBoostFactor *uint64 `protobuf:"varint,3,opt,name=builder_boost_factor,json=builderBoostFactor,proto3,oneof" json:"builder_boost_factor,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } -func (x *SubmitBuilderPreferencesRequest) Reset() { - *x = SubmitBuilderPreferencesRequest{} +func (x *BuilderPreferenceV1) Reset() { + *x = BuilderPreferenceV1{} mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *SubmitBuilderPreferencesRequest) String() string { +func (x *BuilderPreferenceV1) String() string { return protoimpl.X.MessageStringOf(x) } -func (*SubmitBuilderPreferencesRequest) ProtoMessage() {} +func (*BuilderPreferenceV1) ProtoMessage() {} -func (x *SubmitBuilderPreferencesRequest) ProtoReflect() protoreflect.Message { +func (x *BuilderPreferenceV1) ProtoReflect() protoreflect.Message { mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -256,23 +257,30 @@ func (x *SubmitBuilderPreferencesRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use SubmitBuilderPreferencesRequest.ProtoReflect.Descriptor instead. -func (*SubmitBuilderPreferencesRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use BuilderPreferenceV1.ProtoReflect.Descriptor instead. +func (*BuilderPreferenceV1) Descriptor() ([]byte, []int) { return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP(), []int{4} } -func (x *SubmitBuilderPreferencesRequest) GetValidatorPubkey() []byte { +func (x *BuilderPreferenceV1) GetRequest() *BuilderPreferencesRequestV1 { if x != nil { - return x.ValidatorPubkey + return x.Request } return nil } -func (x *SubmitBuilderPreferencesRequest) GetRequest() *BuilderPreferencesRequestV1 { - if x != nil { - return x.Request +func (x *BuilderPreferenceV1) GetMinBid() github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei { + if x != nil && x.MinBid != nil { + return *x.MinBid } - return nil + return github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei(0) +} + +func (x *BuilderPreferenceV1) GetBuilderBoostFactor() uint64 { + if x != nil && x.BuilderBoostFactor != nil { + return *x.BuilderBoostFactor + } + return 0 } var File_proto_prysm_v1alpha1_gloas_builder_api_proto protoreflect.FileDescriptor @@ -320,21 +328,30 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x75, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0x9a, 0x01, 0x0a, 0x1f, - 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, - 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x29, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x31, 0x52, - 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xa3, 0x02, 0x0a, 0x13, + 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, + 0x65, 0x56, 0x31, 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x62, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, + 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, + 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, + 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, + 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, + 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -351,17 +368,17 @@ func file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP() []byte { var file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_proto_prysm_v1alpha1_gloas_builder_api_proto_goTypes = []any{ - (*RequestAuthV1)(nil), // 0: ethereum.eth.v1alpha1.RequestAuthV1 - (*SignedRequestAuthV1)(nil), // 1: ethereum.eth.v1alpha1.SignedRequestAuthV1 - (*BuilderPreferencesV1)(nil), // 2: ethereum.eth.v1alpha1.BuilderPreferencesV1 - (*BuilderPreferencesRequestV1)(nil), // 3: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 - (*SubmitBuilderPreferencesRequest)(nil), // 4: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest + (*RequestAuthV1)(nil), // 0: ethereum.eth.v1alpha1.RequestAuthV1 + (*SignedRequestAuthV1)(nil), // 1: ethereum.eth.v1alpha1.SignedRequestAuthV1 + (*BuilderPreferencesV1)(nil), // 2: ethereum.eth.v1alpha1.BuilderPreferencesV1 + (*BuilderPreferencesRequestV1)(nil), // 3: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 + (*BuilderPreferenceV1)(nil), // 4: ethereum.eth.v1alpha1.BuilderPreferenceV1 } var file_proto_prysm_v1alpha1_gloas_builder_api_proto_depIdxs = []int32{ 0, // 0: ethereum.eth.v1alpha1.SignedRequestAuthV1.message:type_name -> ethereum.eth.v1alpha1.RequestAuthV1 2, // 1: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1.preferences:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesV1 1, // 2: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1.auth:type_name -> ethereum.eth.v1alpha1.SignedRequestAuthV1 - 3, // 3: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 + 3, // 3: ethereum.eth.v1alpha1.BuilderPreferenceV1.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 4, // [4:4] is the sub-list for method output_type 4, // [4:4] is the sub-list for method input_type 4, // [4:4] is the sub-list for extension type_name @@ -374,6 +391,7 @@ func file_proto_prysm_v1alpha1_gloas_builder_api_proto_init() { if File_proto_prysm_v1alpha1_gloas_builder_api_proto != nil { return } + file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index aaeb182a430b..ae031f143c2d 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -43,9 +43,15 @@ message BuilderPreferencesRequestV1 { SignedRequestAuthV1 auth = 2; } -// SubmitBuilderPreferencesRequest is the validator-client gRPC request to forward -// a proposer's builder preferences to the configured builder. -message SubmitBuilderPreferencesRequest { - bytes validator_pubkey = 1; - BuilderPreferencesRequestV1 request = 2; +// BuilderPreferenceV1 is one builder's preferences for a single block proposal, +// carried inline on the block request (JIT model, beacon-APIs #625). request is +// the signed preferences plus auth the beacon node forwards to the builder; +// min_bid and builder_boost_factor are proposer-local and only gate bid selection. +message BuilderPreferenceV1 { + BuilderPreferencesRequestV1 request = 1; + optional uint64 min_bid = 2 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei" + ]; + optional uint64 builder_boost_factor = 3; } diff --git a/proto/prysm/v1alpha1/validator-client/keymanager.pb.go b/proto/prysm/v1alpha1/validator-client/keymanager.pb.go index c07eebf76de6..4d4aedcde71c 100755 --- a/proto/prysm/v1alpha1/validator-client/keymanager.pb.go +++ b/proto/prysm/v1alpha1/validator-client/keymanager.pb.go @@ -712,11 +712,15 @@ func (x *ProposerOptionPayload) GetGasLimit() github_com_OffchainLabs_prysm_v7_c } type BuilderConfig struct { - state protoimpl.MessageState `protogen:"open.v1"` - Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"` - GasLimit github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` - Relays []string `protobuf:"bytes,3,rep,name=relays,proto3" json:"relays,omitempty"` - MaxExecutionPayment github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,4,opt,name=max_execution_payment,json=maxExecutionPayment,proto3" json:"max_execution_payment,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + state protoimpl.MessageState `protogen:"open.v1"` + Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` + GasLimit github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + Relays []string `protobuf:"bytes,3,rep,name=relays,proto3" json:"relays,omitempty"` + MaxExecutionPayment github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,4,opt,name=max_execution_payment,json=maxExecutionPayment,proto3" json:"max_execution_payment,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + Builders []*BuilderEntry `protobuf:"bytes,5,rep,name=builders,proto3" json:"builders,omitempty"` + Proxy *string `protobuf:"bytes,6,opt,name=proxy,proto3,oneof" json:"proxy,omitempty"` + MinBid *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,7,opt,name=min_bid,json=minBid,proto3,oneof" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + BuilderBoostFactor *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,8,opt,name=builder_boost_factor,json=builderBoostFactor,proto3,oneof" json:"builder_boost_factor,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -752,8 +756,8 @@ func (*BuilderConfig) Descriptor() ([]byte, []int) { } func (x *BuilderConfig) GetEnabled() bool { - if x != nil { - return x.Enabled + if x != nil && x.Enabled != nil { + return *x.Enabled } return false } @@ -779,6 +783,126 @@ func (x *BuilderConfig) GetMaxExecutionPayment() github_com_OffchainLabs_prysm_v return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) } +func (x *BuilderConfig) GetBuilders() []*BuilderEntry { + if x != nil { + return x.Builders + } + return nil +} + +func (x *BuilderConfig) GetProxy() string { + if x != nil && x.Proxy != nil { + return *x.Proxy + } + return "" +} + +func (x *BuilderConfig) GetMinBid() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { + if x != nil && x.MinBid != nil { + return *x.MinBid + } + return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) +} + +func (x *BuilderConfig) GetBuilderBoostFactor() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { + if x != nil && x.BuilderBoostFactor != nil { + return *x.BuilderBoostFactor + } + return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) +} + +type BuilderEntry struct { + state protoimpl.MessageState `protogen:"open.v1"` + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` + Pubkey []byte `protobuf:"bytes,2,opt,name=pubkey,proto3,oneof" json:"pubkey,omitempty"` + AuthData []byte `protobuf:"bytes,3,opt,name=auth_data,json=authData,proto3,oneof" json:"auth_data,omitempty"` + Proxy *string `protobuf:"bytes,4,opt,name=proxy,proto3,oneof" json:"proxy,omitempty"` + MinBid *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,5,opt,name=min_bid,json=minBid,proto3,oneof" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + MaxExecutionPayment *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,6,opt,name=max_execution_payment,json=maxExecutionPayment,proto3,oneof" json:"max_execution_payment,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + BuilderBoostFactor *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,7,opt,name=builder_boost_factor,json=builderBoostFactor,proto3,oneof" json:"builder_boost_factor,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *BuilderEntry) Reset() { + *x = BuilderEntry{} + mi := &file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *BuilderEntry) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BuilderEntry) ProtoMessage() {} + +func (x *BuilderEntry) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BuilderEntry.ProtoReflect.Descriptor instead. +func (*BuilderEntry) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDescGZIP(), []int{4} +} + +func (x *BuilderEntry) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *BuilderEntry) GetPubkey() []byte { + if x != nil { + return x.Pubkey + } + return nil +} + +func (x *BuilderEntry) GetAuthData() []byte { + if x != nil { + return x.AuthData + } + return nil +} + +func (x *BuilderEntry) GetProxy() string { + if x != nil && x.Proxy != nil { + return *x.Proxy + } + return "" +} + +func (x *BuilderEntry) GetMinBid() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { + if x != nil && x.MinBid != nil { + return *x.MinBid + } + return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) +} + +func (x *BuilderEntry) GetMaxExecutionPayment() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { + if x != nil && x.MaxExecutionPayment != nil { + return *x.MaxExecutionPayment + } + return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) +} + +func (x *BuilderEntry) GetBuilderBoostFactor() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { + if x != nil && x.BuilderBoostFactor != nil { + return *x.BuilderBoostFactor + } + return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) +} + type ProposerSettingsPayload struct { state protoimpl.MessageState `protogen:"open.v1"` ProposerConfig map[string]*ProposerOptionPayload `protobuf:"bytes,1,rep,name=proposer_config,json=proposerConfig,proto3" json:"proposer_config,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` @@ -790,7 +914,7 @@ type ProposerSettingsPayload struct { func (x *ProposerSettingsPayload) Reset() { *x = ProposerSettingsPayload{} - mi := &file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[4] + mi := &file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -802,7 +926,7 @@ func (x *ProposerSettingsPayload) String() string { func (*ProposerSettingsPayload) ProtoMessage() {} func (x *ProposerSettingsPayload) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[4] + mi := &file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -815,7 +939,7 @@ func (x *ProposerSettingsPayload) ProtoReflect() protoreflect.Message { // Deprecated: Use ProposerSettingsPayload.ProtoReflect.Descriptor instead. func (*ProposerSettingsPayload) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDescGZIP(), []int{4} + return file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDescGZIP(), []int{5} } func (x *ProposerSettingsPayload) GetProposerConfig() map[string]*ProposerOptionPayload { @@ -1068,63 +1192,123 @@ var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDesc = []byte 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x67, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x22, 0xa0, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, - 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, - 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x12, 0x62, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, - 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, - 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x08, 0x67, 0x61, - 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x12, 0x79, - 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, - 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, + 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x22, 0xa8, 0x05, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, + 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x61, + 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x62, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, + 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, + 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6c, + 0x61, 0x79, 0x73, 0x12, 0x79, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, + 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x48, + 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, + 0x32, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x02, 0x52, 0x06, 0x6d, + 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, + 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, + 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, + 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, + 0x65, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, + 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x22, 0xbf, 0x04, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x20, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, + 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, + 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, + 0x88, 0x01, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, + 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x04, 0x52, 0x13, 0x6d, 0x61, 0x78, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, + 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, + 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, + 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x05, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, + 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, + 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, + 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x22, 0x81, 0x03, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, + 0x74, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x78, 0x0a, + 0x13, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xcd, 0x01, 0x0a, 0x22, 0x6f, 0x72, 0x67, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x42, 0x0f, + 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, - 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, - 0x6e, 0x74, 0x36, 0x34, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x81, 0x03, 0x0a, 0x17, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x74, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, - 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x64, - 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x1a, 0x78, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xcd, 0x01, - 0x0a, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, - 0x73, 0x2e, 0x76, 0x32, 0x42, 0x0f, 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, - 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3b, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, 0xaa, 0x02, 0x1e, 0x45, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x1e, 0x45, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x5c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x5c, 0x56, 0x32, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x70, 0x62, 0xaa, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5c, 0x41, 0x63, 0x63, 0x6f, + 0x75, 0x6e, 0x74, 0x73, 0x5c, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1140,74 +1324,76 @@ func file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDescGZIP() [ } var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_goTypes = []any{ (SignResponse_Status)(0), // 0: ethereum.validator.accounts.v2.SignResponse.Status (*SignRequest)(nil), // 1: ethereum.validator.accounts.v2.SignRequest (*SignResponse)(nil), // 2: ethereum.validator.accounts.v2.SignResponse (*ProposerOptionPayload)(nil), // 3: ethereum.validator.accounts.v2.ProposerOptionPayload (*BuilderConfig)(nil), // 4: ethereum.validator.accounts.v2.BuilderConfig - (*ProposerSettingsPayload)(nil), // 5: ethereum.validator.accounts.v2.ProposerSettingsPayload - nil, // 6: ethereum.validator.accounts.v2.ProposerSettingsPayload.ProposerConfigEntry - (*v1alpha1.BeaconBlock)(nil), // 7: ethereum.eth.v1alpha1.BeaconBlock - (*v1alpha1.AttestationData)(nil), // 8: ethereum.eth.v1alpha1.AttestationData - (*v1alpha1.AggregateAttestationAndProof)(nil), // 9: ethereum.eth.v1alpha1.AggregateAttestationAndProof - (*v1alpha1.VoluntaryExit)(nil), // 10: ethereum.eth.v1alpha1.VoluntaryExit - (*v1alpha1.BeaconBlockAltair)(nil), // 11: ethereum.eth.v1alpha1.BeaconBlockAltair - (*v1alpha1.SyncAggregatorSelectionData)(nil), // 12: ethereum.eth.v1alpha1.SyncAggregatorSelectionData - (*v1alpha1.ContributionAndProof)(nil), // 13: ethereum.eth.v1alpha1.ContributionAndProof - (*v1alpha1.BeaconBlockBellatrix)(nil), // 14: ethereum.eth.v1alpha1.BeaconBlockBellatrix - (*v1alpha1.BlindedBeaconBlockBellatrix)(nil), // 15: ethereum.eth.v1alpha1.BlindedBeaconBlockBellatrix - (*v1alpha1.ValidatorRegistrationV1)(nil), // 16: ethereum.eth.v1alpha1.ValidatorRegistrationV1 - (*v1alpha1.BeaconBlockCapella)(nil), // 17: ethereum.eth.v1alpha1.BeaconBlockCapella - (*v1alpha1.BlindedBeaconBlockCapella)(nil), // 18: ethereum.eth.v1alpha1.BlindedBeaconBlockCapella - (*v1alpha1.BeaconBlockDeneb)(nil), // 19: ethereum.eth.v1alpha1.BeaconBlockDeneb - (*v1alpha1.BlindedBeaconBlockDeneb)(nil), // 20: ethereum.eth.v1alpha1.BlindedBeaconBlockDeneb - (*v1alpha1.BeaconBlockElectra)(nil), // 21: ethereum.eth.v1alpha1.BeaconBlockElectra - (*v1alpha1.BlindedBeaconBlockElectra)(nil), // 22: ethereum.eth.v1alpha1.BlindedBeaconBlockElectra - (*v1alpha1.AggregateAttestationAndProofElectra)(nil), // 23: ethereum.eth.v1alpha1.AggregateAttestationAndProofElectra - (*v1alpha1.BlindedBeaconBlockFulu)(nil), // 24: ethereum.eth.v1alpha1.BlindedBeaconBlockFulu - (*v1alpha1.BeaconBlockGloas)(nil), // 25: ethereum.eth.v1alpha1.BeaconBlockGloas - (*v1alpha1.ExecutionPayloadEnvelope)(nil), // 26: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope - (*v1alpha1.PayloadAttestationData)(nil), // 27: ethereum.eth.v1alpha1.PayloadAttestationData - (*v1alpha1.ProposerPreferences)(nil), // 28: ethereum.eth.v1alpha1.ProposerPreferences - (*v1alpha1.RequestAuthV1)(nil), // 29: ethereum.eth.v1alpha1.RequestAuthV1 + (*BuilderEntry)(nil), // 5: ethereum.validator.accounts.v2.BuilderEntry + (*ProposerSettingsPayload)(nil), // 6: ethereum.validator.accounts.v2.ProposerSettingsPayload + nil, // 7: ethereum.validator.accounts.v2.ProposerSettingsPayload.ProposerConfigEntry + (*v1alpha1.BeaconBlock)(nil), // 8: ethereum.eth.v1alpha1.BeaconBlock + (*v1alpha1.AttestationData)(nil), // 9: ethereum.eth.v1alpha1.AttestationData + (*v1alpha1.AggregateAttestationAndProof)(nil), // 10: ethereum.eth.v1alpha1.AggregateAttestationAndProof + (*v1alpha1.VoluntaryExit)(nil), // 11: ethereum.eth.v1alpha1.VoluntaryExit + (*v1alpha1.BeaconBlockAltair)(nil), // 12: ethereum.eth.v1alpha1.BeaconBlockAltair + (*v1alpha1.SyncAggregatorSelectionData)(nil), // 13: ethereum.eth.v1alpha1.SyncAggregatorSelectionData + (*v1alpha1.ContributionAndProof)(nil), // 14: ethereum.eth.v1alpha1.ContributionAndProof + (*v1alpha1.BeaconBlockBellatrix)(nil), // 15: ethereum.eth.v1alpha1.BeaconBlockBellatrix + (*v1alpha1.BlindedBeaconBlockBellatrix)(nil), // 16: ethereum.eth.v1alpha1.BlindedBeaconBlockBellatrix + (*v1alpha1.ValidatorRegistrationV1)(nil), // 17: ethereum.eth.v1alpha1.ValidatorRegistrationV1 + (*v1alpha1.BeaconBlockCapella)(nil), // 18: ethereum.eth.v1alpha1.BeaconBlockCapella + (*v1alpha1.BlindedBeaconBlockCapella)(nil), // 19: ethereum.eth.v1alpha1.BlindedBeaconBlockCapella + (*v1alpha1.BeaconBlockDeneb)(nil), // 20: ethereum.eth.v1alpha1.BeaconBlockDeneb + (*v1alpha1.BlindedBeaconBlockDeneb)(nil), // 21: ethereum.eth.v1alpha1.BlindedBeaconBlockDeneb + (*v1alpha1.BeaconBlockElectra)(nil), // 22: ethereum.eth.v1alpha1.BeaconBlockElectra + (*v1alpha1.BlindedBeaconBlockElectra)(nil), // 23: ethereum.eth.v1alpha1.BlindedBeaconBlockElectra + (*v1alpha1.AggregateAttestationAndProofElectra)(nil), // 24: ethereum.eth.v1alpha1.AggregateAttestationAndProofElectra + (*v1alpha1.BlindedBeaconBlockFulu)(nil), // 25: ethereum.eth.v1alpha1.BlindedBeaconBlockFulu + (*v1alpha1.BeaconBlockGloas)(nil), // 26: ethereum.eth.v1alpha1.BeaconBlockGloas + (*v1alpha1.ExecutionPayloadEnvelope)(nil), // 27: ethereum.eth.v1alpha1.ExecutionPayloadEnvelope + (*v1alpha1.PayloadAttestationData)(nil), // 28: ethereum.eth.v1alpha1.PayloadAttestationData + (*v1alpha1.ProposerPreferences)(nil), // 29: ethereum.eth.v1alpha1.ProposerPreferences + (*v1alpha1.RequestAuthV1)(nil), // 30: ethereum.eth.v1alpha1.RequestAuthV1 } var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_depIdxs = []int32{ - 7, // 0: ethereum.validator.accounts.v2.SignRequest.block:type_name -> ethereum.eth.v1alpha1.BeaconBlock - 8, // 1: ethereum.validator.accounts.v2.SignRequest.attestation_data:type_name -> ethereum.eth.v1alpha1.AttestationData - 9, // 2: ethereum.validator.accounts.v2.SignRequest.aggregate_attestation_and_proof:type_name -> ethereum.eth.v1alpha1.AggregateAttestationAndProof - 10, // 3: ethereum.validator.accounts.v2.SignRequest.exit:type_name -> ethereum.eth.v1alpha1.VoluntaryExit - 11, // 4: ethereum.validator.accounts.v2.SignRequest.block_altair:type_name -> ethereum.eth.v1alpha1.BeaconBlockAltair - 12, // 5: ethereum.validator.accounts.v2.SignRequest.sync_aggregator_selection_data:type_name -> ethereum.eth.v1alpha1.SyncAggregatorSelectionData - 13, // 6: ethereum.validator.accounts.v2.SignRequest.contribution_and_proof:type_name -> ethereum.eth.v1alpha1.ContributionAndProof - 14, // 7: ethereum.validator.accounts.v2.SignRequest.block_bellatrix:type_name -> ethereum.eth.v1alpha1.BeaconBlockBellatrix - 15, // 8: ethereum.validator.accounts.v2.SignRequest.blinded_block_bellatrix:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockBellatrix - 16, // 9: ethereum.validator.accounts.v2.SignRequest.registration:type_name -> ethereum.eth.v1alpha1.ValidatorRegistrationV1 - 17, // 10: ethereum.validator.accounts.v2.SignRequest.block_capella:type_name -> ethereum.eth.v1alpha1.BeaconBlockCapella - 18, // 11: ethereum.validator.accounts.v2.SignRequest.blinded_block_capella:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockCapella - 19, // 12: ethereum.validator.accounts.v2.SignRequest.block_deneb:type_name -> ethereum.eth.v1alpha1.BeaconBlockDeneb - 20, // 13: ethereum.validator.accounts.v2.SignRequest.blinded_block_deneb:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockDeneb - 21, // 14: ethereum.validator.accounts.v2.SignRequest.block_electra:type_name -> ethereum.eth.v1alpha1.BeaconBlockElectra - 22, // 15: ethereum.validator.accounts.v2.SignRequest.blinded_block_electra:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockElectra - 23, // 16: ethereum.validator.accounts.v2.SignRequest.aggregate_attestation_and_proof_electra:type_name -> ethereum.eth.v1alpha1.AggregateAttestationAndProofElectra - 21, // 17: ethereum.validator.accounts.v2.SignRequest.block_fulu:type_name -> ethereum.eth.v1alpha1.BeaconBlockElectra - 24, // 18: ethereum.validator.accounts.v2.SignRequest.blinded_block_fulu:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockFulu - 25, // 19: ethereum.validator.accounts.v2.SignRequest.block_gloas:type_name -> ethereum.eth.v1alpha1.BeaconBlockGloas - 26, // 20: ethereum.validator.accounts.v2.SignRequest.execution_payload_envelope:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelope - 27, // 21: ethereum.validator.accounts.v2.SignRequest.payload_attestation_data:type_name -> ethereum.eth.v1alpha1.PayloadAttestationData - 28, // 22: ethereum.validator.accounts.v2.SignRequest.proposer_preference:type_name -> ethereum.eth.v1alpha1.ProposerPreferences - 29, // 23: ethereum.validator.accounts.v2.SignRequest.request_auth:type_name -> ethereum.eth.v1alpha1.RequestAuthV1 + 8, // 0: ethereum.validator.accounts.v2.SignRequest.block:type_name -> ethereum.eth.v1alpha1.BeaconBlock + 9, // 1: ethereum.validator.accounts.v2.SignRequest.attestation_data:type_name -> ethereum.eth.v1alpha1.AttestationData + 10, // 2: ethereum.validator.accounts.v2.SignRequest.aggregate_attestation_and_proof:type_name -> ethereum.eth.v1alpha1.AggregateAttestationAndProof + 11, // 3: ethereum.validator.accounts.v2.SignRequest.exit:type_name -> ethereum.eth.v1alpha1.VoluntaryExit + 12, // 4: ethereum.validator.accounts.v2.SignRequest.block_altair:type_name -> ethereum.eth.v1alpha1.BeaconBlockAltair + 13, // 5: ethereum.validator.accounts.v2.SignRequest.sync_aggregator_selection_data:type_name -> ethereum.eth.v1alpha1.SyncAggregatorSelectionData + 14, // 6: ethereum.validator.accounts.v2.SignRequest.contribution_and_proof:type_name -> ethereum.eth.v1alpha1.ContributionAndProof + 15, // 7: ethereum.validator.accounts.v2.SignRequest.block_bellatrix:type_name -> ethereum.eth.v1alpha1.BeaconBlockBellatrix + 16, // 8: ethereum.validator.accounts.v2.SignRequest.blinded_block_bellatrix:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockBellatrix + 17, // 9: ethereum.validator.accounts.v2.SignRequest.registration:type_name -> ethereum.eth.v1alpha1.ValidatorRegistrationV1 + 18, // 10: ethereum.validator.accounts.v2.SignRequest.block_capella:type_name -> ethereum.eth.v1alpha1.BeaconBlockCapella + 19, // 11: ethereum.validator.accounts.v2.SignRequest.blinded_block_capella:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockCapella + 20, // 12: ethereum.validator.accounts.v2.SignRequest.block_deneb:type_name -> ethereum.eth.v1alpha1.BeaconBlockDeneb + 21, // 13: ethereum.validator.accounts.v2.SignRequest.blinded_block_deneb:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockDeneb + 22, // 14: ethereum.validator.accounts.v2.SignRequest.block_electra:type_name -> ethereum.eth.v1alpha1.BeaconBlockElectra + 23, // 15: ethereum.validator.accounts.v2.SignRequest.blinded_block_electra:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockElectra + 24, // 16: ethereum.validator.accounts.v2.SignRequest.aggregate_attestation_and_proof_electra:type_name -> ethereum.eth.v1alpha1.AggregateAttestationAndProofElectra + 22, // 17: ethereum.validator.accounts.v2.SignRequest.block_fulu:type_name -> ethereum.eth.v1alpha1.BeaconBlockElectra + 25, // 18: ethereum.validator.accounts.v2.SignRequest.blinded_block_fulu:type_name -> ethereum.eth.v1alpha1.BlindedBeaconBlockFulu + 26, // 19: ethereum.validator.accounts.v2.SignRequest.block_gloas:type_name -> ethereum.eth.v1alpha1.BeaconBlockGloas + 27, // 20: ethereum.validator.accounts.v2.SignRequest.execution_payload_envelope:type_name -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelope + 28, // 21: ethereum.validator.accounts.v2.SignRequest.payload_attestation_data:type_name -> ethereum.eth.v1alpha1.PayloadAttestationData + 29, // 22: ethereum.validator.accounts.v2.SignRequest.proposer_preference:type_name -> ethereum.eth.v1alpha1.ProposerPreferences + 30, // 23: ethereum.validator.accounts.v2.SignRequest.request_auth:type_name -> ethereum.eth.v1alpha1.RequestAuthV1 0, // 24: ethereum.validator.accounts.v2.SignResponse.status:type_name -> ethereum.validator.accounts.v2.SignResponse.Status 4, // 25: ethereum.validator.accounts.v2.ProposerOptionPayload.builder:type_name -> ethereum.validator.accounts.v2.BuilderConfig - 6, // 26: ethereum.validator.accounts.v2.ProposerSettingsPayload.proposer_config:type_name -> ethereum.validator.accounts.v2.ProposerSettingsPayload.ProposerConfigEntry - 3, // 27: ethereum.validator.accounts.v2.ProposerSettingsPayload.default_config:type_name -> ethereum.validator.accounts.v2.ProposerOptionPayload - 3, // 28: ethereum.validator.accounts.v2.ProposerSettingsPayload.ProposerConfigEntry.value:type_name -> ethereum.validator.accounts.v2.ProposerOptionPayload - 29, // [29:29] is the sub-list for method output_type - 29, // [29:29] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 5, // 26: ethereum.validator.accounts.v2.BuilderConfig.builders:type_name -> ethereum.validator.accounts.v2.BuilderEntry + 7, // 27: ethereum.validator.accounts.v2.ProposerSettingsPayload.proposer_config:type_name -> ethereum.validator.accounts.v2.ProposerSettingsPayload.ProposerConfigEntry + 3, // 28: ethereum.validator.accounts.v2.ProposerSettingsPayload.default_config:type_name -> ethereum.validator.accounts.v2.ProposerOptionPayload + 3, // 29: ethereum.validator.accounts.v2.ProposerSettingsPayload.ProposerConfigEntry.value:type_name -> ethereum.validator.accounts.v2.ProposerOptionPayload + 30, // [30:30] is the sub-list for method output_type + 30, // [30:30] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_proto_prysm_v1alpha1_validator_client_keymanager_proto_init() } @@ -1245,13 +1431,15 @@ func file_proto_prysm_v1alpha1_validator_client_keymanager_proto_init() { (*SignRequest_RequestAuth)(nil), } file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[2].OneofWrappers = []any{} + file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[3].OneofWrappers = []any{} + file_proto_prysm_v1alpha1_validator_client_keymanager_proto_msgTypes[4].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDesc, NumEnums: 1, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/prysm/v1alpha1/validator-client/keymanager.proto b/proto/prysm/v1alpha1/validator-client/keymanager.proto index 688f1ce37bdb..1fd2450f560c 100644 --- a/proto/prysm/v1alpha1/validator-client/keymanager.proto +++ b/proto/prysm/v1alpha1/validator-client/keymanager.proto @@ -129,7 +129,7 @@ message ProposerOptionPayload { // BuilderConfig is a property of ProposerOptionPayload message BuilderConfig { - bool enabled = 1; + optional bool enabled = 1; uint64 gas_limit = 2 [ (ethereum.eth.ext.cast_type) = "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" @@ -142,6 +142,39 @@ message BuilderConfig { (ethereum.eth.ext.cast_type) = "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" ]; + // builders is the per-key builder list from the keymanager validator config + // endpoint. Each entry may override the builder-level fallbacks below. + repeated BuilderEntry builders = 5; + optional string proxy = 6; + optional uint64 min_bid = 7 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" + ]; + optional uint64 builder_boost_factor = 8 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" + ]; +} + +// BuilderEntry is a single builder in a proposer's per-key builder list. Fields +// left unset inherit from the enclosing BuilderConfig, then default_config. +message BuilderEntry { + string url = 1; + optional bytes pubkey = 2; + optional bytes auth_data = 3; + optional string proxy = 4; + optional uint64 min_bid = 5 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" + ]; + optional uint64 max_execution_payment = 6 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" + ]; + optional uint64 builder_boost_factor = 7 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" + ]; } // ProposerSettingsPayload is used to unmarshal files sent from the validator diff --git a/proto/prysm/v1alpha1/validator.pb.go b/proto/prysm/v1alpha1/validator.pb.go index 3e3501516746..e67df0e78228 100755 --- a/proto/prysm/v1alpha1/validator.pb.go +++ b/proto/prysm/v1alpha1/validator.pb.go @@ -1344,7 +1344,7 @@ type BlockRequest struct { SkipMevBoost bool `protobuf:"varint,4,opt,name=skip_mev_boost,json=skipMevBoost,proto3" json:"skip_mev_boost,omitempty"` BuilderBoostFactor *wrapperspb.UInt64Value `protobuf:"bytes,5,opt,name=builder_boost_factor,json=builderBoostFactor,proto3" json:"builder_boost_factor,omitempty"` EagerPayloadStateRoot bool `protobuf:"varint,6,opt,name=eager_payload_state_root,json=eagerPayloadStateRoot,proto3" json:"eager_payload_state_root,omitempty"` - BuilderRequestAuths []*SignedRequestAuthV1 `protobuf:"bytes,7,rep,name=builder_request_auths,json=builderRequestAuths,proto3" json:"builder_request_auths,omitempty"` + BuilderPreferences []*BuilderPreferenceV1 `protobuf:"bytes,8,rep,name=builder_preferences,json=builderPreferences,proto3" json:"builder_preferences,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1421,9 +1421,9 @@ func (x *BlockRequest) GetEagerPayloadStateRoot() bool { return false } -func (x *BlockRequest) GetBuilderRequestAuths() []*SignedRequestAuthV1 { +func (x *BlockRequest) GetBuilderPreferences() []*BuilderPreferenceV1 { if x != nil { - return x.BuilderRequestAuths + return x.BuilderPreferences } return nil } @@ -4402,7 +4402,7 @@ var file_proto_prysm_v1alpha1_validator_proto_rawDesc = []byte{ 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x08, 0x70, 0x74, 0x63, 0x53, 0x6c, 0x6f, 0x74, - 0x73, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xcc, 0x03, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x73, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xcf, 0x03, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, @@ -4425,997 +4425,987 @@ var file_proto_prysm_v1alpha1_validator_proto_rawDesc = []byte{ 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x15, 0x65, 0x61, 0x67, 0x65, 0x72, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, - 0x12, 0x5e, 0x0a, 0x15, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x13, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x75, 0x74, 0x68, 0x73, - 0x3a, 0x02, 0x18, 0x01, 0x22, 0x3c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x33, 0x32, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x3a, 0x02, - 0x18, 0x01, 0x22, 0x3e, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x45, 0x78, 0x69, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x09, 0x65, 0x78, 0x69, - 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x3a, 0x02, - 0x18, 0x01, 0x22, 0xef, 0x01, 0x0a, 0x16, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, - 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, - 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, - 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, - 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x77, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, - 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, - 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x3a, 0x02, 0x18, 0x01, 0x22, 0x50, 0x0a, 0x0e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x13, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x6f, - 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xd2, 0x02, 0x0a, 0x19, 0x41, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x77, - 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, - 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, - 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, - 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, - 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x09, 0x70, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x0e, 0x73, 0x6c, 0x6f, 0x74, - 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0d, 0x73, 0x6c, 0x6f, 0x74, 0x53, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x85, 0x01, 0x0a, 0x1a, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x13, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, - 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x11, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x3a, - 0x02, 0x18, 0x01, 0x22, 0x93, 0x01, 0x0a, 0x21, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x13, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x45, 0x6c, 0x65, 0x63, 0x74, - 0x72, 0x61, 0x52, 0x11, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, - 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x9a, 0x01, 0x0a, 0x1c, 0x53, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, - 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x76, 0x0a, 0x1a, 0x73, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, - 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x17, 0x73, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, - 0x6f, 0x66, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xa8, 0x01, 0x0a, 0x23, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x7d, - 0x0a, 0x1a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x45, 0x6c, 0x65, - 0x63, 0x74, 0x72, 0x61, 0x52, 0x17, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x3a, 0x02, 0x18, - 0x01, 0x22, 0x5f, 0x0a, 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x13, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x3a, 0x02, - 0x18, 0x01, 0x22, 0xc7, 0x03, 0x0a, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, - 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, - 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, - 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x05, 0x73, 0x6c, - 0x6f, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, - 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, - 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, 0x73, 0x5f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x08, 0x52, - 0x0c, 0x69, 0x73, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x7b, 0x0a, - 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, - 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, - 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, - 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x5f, 0x73, 0x6c, 0x6f, 0x74, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x04, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, - 0x65, 0x73, 0x41, 0x74, 0x53, 0x6c, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x8e, 0x05, 0x0a, - 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, - 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x19, 0x67, 0x6c, 0x6f, 0x62, 0x61, - 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x42, 0x02, 0x18, 0x01, 0x52, 0x17, - 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0b, 0x76, 0x6f, 0x74, 0x65, 0x64, - 0x5f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x0a, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x45, 0x74, 0x68, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x0e, - 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, - 0x6c, 0x65, 0x45, 0x74, 0x68, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x75, 0x72, 0x72, 0x65, - 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x67, 0x77, 0x65, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x75, 0x72, 0x72, - 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x77, - 0x65, 0x69, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, - 0x65, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, - 0x77, 0x65, 0x69, 0x12, 0x4c, 0x0a, 0x23, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, - 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x1f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, - 0x69, 0x12, 0x3b, 0x0a, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, - 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x77, 0x65, 0x69, 0x12, 0x41, - 0x0a, 0x1d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, - 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, - 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, - 0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, - 0x69, 0x12, 0x4a, 0x0a, 0x22, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, - 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1e, 0x70, - 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x22, 0x8b, 0x02, - 0x0a, 0x11, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x12, 0x64, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, - 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x70, 0x0a, 0x10, 0x61, 0x63, 0x74, - 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x12, 0x5b, 0x0a, 0x13, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x56, 0x31, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x3a, 0x02, 0x18, + 0x01, 0x4a, 0x04, 0x08, 0x07, 0x10, 0x08, 0x22, 0x3c, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, + 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x3e, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x45, 0x78, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x09, + 0x65, 0x78, 0x69, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x52, 0x6f, 0x6f, + 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xef, 0x01, 0x0a, 0x16, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, + 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x77, 0x0a, 0x0f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0f, 0x61, 0x63, 0x74, 0x69, - 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x22, 0x92, 0x01, 0x0a, 0x10, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x64, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, - 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, - 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x22, 0xd1, 0x02, 0x0a, 0x13, 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x12, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x70, - 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x52, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x73, 0x1a, 0xc9, 0x01, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, - 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, - 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, - 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, - 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, - 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x27, 0x0a, 0x0b, 0x73, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x6f, 0x6f, 0x74, - 0x3a, 0x02, 0x18, 0x01, 0x22, 0xe8, 0x01, 0x0a, 0x14, 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, - 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x3d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, - 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, - 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x6f, 0x0a, 0x11, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, - 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, - 0x12, 0x29, 0x0a, 0x10, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x78, - 0x69, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, 0x75, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x3a, 0x02, 0x18, 0x01, 0x22, - 0x3d, 0x0a, 0x12, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x76, 0x65, - 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x3e, - 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x76, 0x65, - 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xcd, - 0x02, 0x0a, 0x1c, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x69, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x70, - 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, - 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x0a, - 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0xbd, 0x01, 0x0a, 0x15, 0x46, - 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x69, - 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x32, 0x30, 0x52, 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, - 0x74, 0x12, 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, - 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x52, - 0x0a, 0x1b, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x79, - 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, - 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x3a, 0x02, - 0x18, 0x01, 0x22, 0x4f, 0x0a, 0x1c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, - 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, - 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, - 0x30, 0x52, 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x3a, - 0x02, 0x18, 0x01, 0x22, 0x95, 0x01, 0x0a, 0x1e, 0x41, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, - 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, - 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x8b, 0x02, 0x0a, 0x26, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, - 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x6d, 0x73, 0x67, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, - 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x04, 0x6d, 0x73, 0x67, 0x73, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x69, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x50, 0x0a, 0x0e, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x15, 0x61, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, + 0x52, 0x13, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, + 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xd2, 0x02, 0x0a, 0x19, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, - 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x49, 0x64, 0x12, 0x25, - 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x68, 0x0a, 0x27, 0x41, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, 0x41, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x61, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x62, - 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x69, 0x74, 0x73, 0x3a, - 0x02, 0x18, 0x01, 0x22, 0xf1, 0x01, 0x0a, 0x15, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, - 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, - 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, - 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, - 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, - 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x7b, 0x0a, 0x11, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, - 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, - 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, - 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x16, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x5f, - 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x33, 0x32, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, - 0x74, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, - 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, - 0x73, 0x74, 0x69, 0x63, 0x12, 0x3b, 0x0a, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x79, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, - 0x73, 0x22, 0x99, 0x04, 0x0a, 0x0c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, - 0x74, 0x79, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, - 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, - 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, - 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x77, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, - 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, - 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, - 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, - 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x29, - 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x6c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x74, 0x65, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, - 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, - 0x73, 0x41, 0x74, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, - 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, - 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, - 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x22, 0x74, 0x0a, - 0x15, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, - 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, - 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, - 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, - 0x6f, 0x63, 0x68, 0x22, 0xb9, 0x01, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, - 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0d, - 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x31, 0x0a, - 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, - 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, - 0x12, 0x3d, 0x0a, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x44, 0x75, 0x74, 0x79, 0x56, 0x32, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x22, - 0x8d, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x79, - 0x56, 0x32, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x74, 0x12, 0x77, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, + 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, + 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2d, 0x0a, 0x0e, 0x73, + 0x6c, 0x6f, 0x74, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0d, 0x73, 0x6c, 0x6f, + 0x74, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x85, + 0x01, 0x0a, 0x1a, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, + 0x13, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, + 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, + 0x11, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x93, 0x01, 0x0a, 0x21, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, 0x65, + 0x63, 0x74, 0x72, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6a, 0x0a, 0x13, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, + 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x45, 0x6c, + 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x11, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x9a, 0x01, 0x0a, + 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x76, 0x0a, + 0x1a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x39, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x52, 0x17, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, + 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xa8, 0x01, 0x0a, 0x23, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x7d, 0x0a, 0x1a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x40, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x17, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, + 0x3a, 0x02, 0x18, 0x01, 0x22, 0x5f, 0x0a, 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3a, 0x0a, 0x15, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x13, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x6f, 0x6f, + 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xc7, 0x03, 0x0a, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5a, 0x0a, 0x05, 0x73, 0x6c, + 0x6f, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, + 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, + 0x05, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x73, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, + 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, + 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, + 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x64, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x69, + 0x73, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x08, 0x52, 0x0c, 0x69, 0x73, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x6f, 0x72, + 0x12, 0x7b, 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, + 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, + 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, + 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x12, 0x2c, 0x0a, + 0x12, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x5f, 0x73, + 0x6c, 0x6f, 0x74, 0x18, 0x05, 0x20, 0x03, 0x28, 0x04, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x74, 0x65, 0x65, 0x73, 0x41, 0x74, 0x53, 0x6c, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, + 0x8e, 0x05, 0x0a, 0x16, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x61, 0x72, + 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x19, 0x67, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x17, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, + 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0b, 0x76, 0x6f, + 0x74, 0x65, 0x64, 0x5f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x02, 0x18, 0x01, 0x52, 0x0a, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x45, 0x74, 0x68, 0x65, 0x72, 0x12, + 0x29, 0x0a, 0x0e, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x65, 0x6c, 0x69, + 0x67, 0x69, 0x62, 0x6c, 0x65, 0x45, 0x74, 0x68, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x75, + 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x65, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, + 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x65, 0x47, 0x77, 0x65, 0x69, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, + 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x63, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x12, 0x4c, 0x0a, 0x23, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x06, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x1f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + 0x47, 0x77, 0x65, 0x69, 0x12, 0x3b, 0x0a, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x77, + 0x65, 0x69, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x77, 0x65, + 0x69, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, + 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, + 0x65, 0x69, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + 0x47, 0x77, 0x65, 0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, + 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + 0x47, 0x77, 0x65, 0x69, 0x12, 0x4a, 0x0a, 0x22, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x1e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, + 0x65, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, + 0x22, 0x8b, 0x02, 0x0a, 0x11, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x64, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, - 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, - 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x22, - 0xf6, 0x01, 0x0a, 0x1a, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, - 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, - 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, - 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, - 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, - 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, - 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x7b, 0x0a, 0x11, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, - 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, - 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x1b, 0x53, 0x79, 0x6e, - 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x12, 0x40, 0x0a, 0x06, 0x64, - 0x75, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, - 0x65, 0x44, 0x75, 0x74, 0x79, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x22, 0xff, 0x01, - 0x0a, 0x11, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, - 0x75, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, - 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, - 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1e, 0x0a, 0x06, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x70, 0x0a, 0x10, + 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, - 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x47, 0x0a, 0x20, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, - 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, - 0x52, 0x1d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x79, 0x6e, 0x63, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, - 0xec, 0x01, 0x0a, 0x10, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, - 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, - 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, - 0x68, 0x12, 0x7b, 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, - 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, + 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x0f, 0x61, + 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x22, 0x92, + 0x01, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x12, 0x64, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, + 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, + 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x22, 0xd1, 0x02, 0x0a, 0x13, 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, 0x61, + 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x6a, 0x0a, 0x12, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x3b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x52, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x1a, 0xc9, 0x01, 0x0a, 0x10, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, + 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, + 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x5b, 0x0a, + 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, + 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xad, - 0x01, 0x0a, 0x11, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x33, 0x32, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x52, - 0x6f, 0x6f, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, - 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x12, 0x36, 0x0a, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, + 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x27, 0x0a, 0x0b, 0x73, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, + 0x6f, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xe8, 0x01, 0x0a, 0x14, 0x44, 0x6f, 0x70, 0x70, + 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x5b, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x3d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x70, 0x70, + 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x73, 0x1a, 0x6f, 0x0a, + 0x11, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, + 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x64, + 0x75, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x65, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x3a, 0x02, + 0x18, 0x01, 0x22, 0x3d, 0x0a, 0x12, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x3a, 0x02, 0x18, + 0x01, 0x22, 0x3e, 0x0a, 0x13, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x76, 0x65, 0x72, 0x69, + 0x66, 0x69, 0x65, 0x64, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0c, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x3a, 0x02, 0x18, + 0x01, 0x22, 0xcd, 0x02, 0x0a, 0x1c, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x69, 0x0a, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x49, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, - 0x54, 0x43, 0x44, 0x75, 0x74, 0x79, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x22, 0x86, - 0x02, 0x0a, 0x07, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, - 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x46, 0x65, 0x65, 0x52, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, + 0x72, 0x52, 0x0a, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0xbd, 0x01, + 0x0a, 0x15, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, + 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, + 0x69, 0x65, 0x6e, 0x74, 0x12, 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x58, 0x0a, - 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, - 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, - 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, - 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x22, 0x7f, 0x0a, 0x1f, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, - 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, - 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x02, 0x18, + 0x01, 0x22, 0x52, 0x0a, 0x1b, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x79, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x4f, 0x0a, 0x1c, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, + 0x70, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, + 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x32, 0x30, 0x52, 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, + 0x6e, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x95, 0x01, 0x0a, 0x1e, 0x41, 0x73, 0x73, 0x69, 0x67, + 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x53, 0x75, 0x62, 0x6e, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2f, 0x0a, 0x0a, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, + 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, + 0x09, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x8b, + 0x02, 0x0a, 0x26, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, + 0x41, 0x6e, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, + 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x6d, 0x73, 0x67, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x04, 0x6d, 0x73, 0x67, 0x73, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, + 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, - 0x73, 0x6c, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x73, 0x0a, 0x20, 0x45, 0x78, 0x65, 0x63, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, - 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, - 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, - 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x7d, 0x0a, - 0x1d, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, - 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, - 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, - 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, - 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x2a, 0x9a, 0x01, 0x0a, - 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, - 0x55, 0x53, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, - 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x02, - 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x12, 0x0b, 0x0a, 0x07, - 0x45, 0x58, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x53, 0x4c, 0x41, - 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x58, 0x49, 0x54, 0x45, - 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x07, - 0x12, 0x17, 0x0a, 0x13, 0x50, 0x41, 0x52, 0x54, 0x49, 0x41, 0x4c, 0x4c, 0x59, 0x5f, 0x44, 0x45, - 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x44, 0x10, 0x08, 0x32, 0xf3, 0x39, 0x0a, 0x13, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x12, - 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x75, - 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, - 0x74, 0x69, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, 0x89, 0x01, 0x0a, 0x0b, 0x47, 0x65, 0x74, 0x44, - 0x75, 0x74, 0x69, 0x65, 0x73, 0x56, 0x32, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x56, 0x32, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, - 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x56, 0x32, - 0x88, 0x02, 0x01, 0x12, 0xa4, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x49, + 0x64, 0x12, 0x25, 0x0a, 0x0a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x68, 0x0a, 0x27, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0d, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x12, 0x12, + 0x0a, 0x04, 0x62, 0x69, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x62, 0x69, + 0x74, 0x73, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xf1, 0x01, 0x0a, 0x15, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x7b, 0x0a, + 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0xb7, 0x01, 0x0a, 0x16, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, + 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, + 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x12, 0x3b, 0x0a, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x12, 0x27, - 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x88, 0x02, 0x01, 0x12, 0xa6, 0x01, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, - 0x56, 0x32, 0x12, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x12, 0x27, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x88, 0x02, 0x01, 0x12, 0xb9, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x12, 0x31, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x79, 0x52, 0x06, 0x64, 0x75, + 0x74, 0x69, 0x65, 0x73, 0x22, 0x99, 0x04, 0x0a, 0x0c, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x44, 0x75, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, + 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, + 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x77, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, + 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x2c, 0x0a, 0x12, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x5f, 0x73, 0x6c, + 0x6f, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x73, 0x41, 0x74, 0x53, 0x6c, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x19, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, + 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, + 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, + 0x22, 0x74, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, 0x6f, + 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, + 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x22, 0xb9, 0x01, 0x0a, 0x16, 0x50, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2d, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x5f, 0x72, + 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, + 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, + 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x12, 0x3d, 0x0a, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x79, 0x56, 0x32, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, + 0x65, 0x73, 0x22, 0x8d, 0x02, 0x0a, 0x0e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, + 0x75, 0x74, 0x79, 0x56, 0x32, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, + 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, + 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, + 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, + 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, + 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, + 0x6f, 0x74, 0x22, 0xf6, 0x01, 0x0a, 0x1a, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x12, 0x7b, + 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, + 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, + 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x1b, + 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x14, 0x65, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, + 0x74, 0x69, 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x12, 0x40, + 0x0a, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x12, 0x2d, 0x2f, - 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x73, 0x79, - 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x88, 0x02, 0x01, 0x12, - 0x90, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, - 0x12, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x79, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, + 0x22, 0xff, 0x01, 0x0a, 0x11, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, + 0x65, 0x65, 0x44, 0x75, 0x74, 0x79, 0x12, 0x28, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, + 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x12, 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, + 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x47, 0x0a, 0x20, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x03, 0x20, + 0x03, 0x28, 0x04, 0x52, 0x1d, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x79, + 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x69, 0x63, + 0x65, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x10, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x05, 0x65, 0x70, 0x6f, 0x63, 0x68, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, + 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, + 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, + 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x52, 0x05, 0x65, + 0x70, 0x6f, 0x63, 0x68, 0x12, 0x7b, 0x0a, 0x11, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, + 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, + 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, + 0x73, 0x22, 0xad, 0x01, 0x0a, 0x11, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x0e, 0x64, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0d, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, + 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x31, 0x0a, 0x14, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4f, + 0x70, 0x74, 0x69, 0x6d, 0x69, 0x73, 0x74, 0x69, 0x63, 0x12, 0x36, 0x0a, 0x06, 0x64, 0x75, 0x74, + 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, 0x2f, 0x65, 0x74, - 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x74, 0x63, 0x88, - 0x02, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x0a, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x44, 0x61, 0x74, - 0x61, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, - 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x88, 0x02, 0x01, 0x12, 0x8e, 0x01, 0x0a, 0x11, 0x57, 0x61, - 0x69, 0x74, 0x46, 0x6f, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, - 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x65, 0x74, 0x68, + 0x31, 0x2e, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x79, 0x52, 0x06, 0x64, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x22, 0x86, 0x02, 0x0a, 0x07, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x79, 0x12, 0x28, 0x0a, + 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x10, 0x8a, + 0xb5, 0x18, 0x02, 0x34, 0x38, 0x9a, 0xb5, 0x18, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x52, + 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x77, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x52, 0x0e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, + 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x22, 0x7f, 0x0a, 0x1f, 0x45, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, + 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x58, 0x0a, + 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, + 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, + 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, + 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x22, 0x73, 0x0a, 0x20, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, + 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x4b, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, + 0x70, 0x65, 0x52, 0x08, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x3a, 0x02, 0x18, 0x01, + 0x22, 0x7d, 0x0a, 0x1d, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x3a, 0x02, 0x18, 0x01, 0x2a, + 0x9a, 0x01, 0x0a, 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x44, 0x45, 0x50, 0x4f, 0x53, + 0x49, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, + 0x47, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x03, 0x12, + 0x0b, 0x0a, 0x07, 0x45, 0x58, 0x49, 0x54, 0x49, 0x4e, 0x47, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, + 0x53, 0x4c, 0x41, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x58, + 0x49, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, + 0x44, 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x41, 0x52, 0x54, 0x49, 0x41, 0x4c, 0x4c, 0x59, + 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x44, 0x10, 0x08, 0x32, 0xcb, 0x38, 0x0a, + 0x13, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x75, 0x74, 0x69, + 0x65, 0x73, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, 0x89, 0x01, 0x0a, 0x0b, 0x47, + 0x65, 0x74, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x56, 0x32, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x56, + 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x22, 0x12, 0x20, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x56, 0x32, 0x88, 0x02, 0x01, 0x12, 0xa4, 0x01, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2c, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, + 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x29, 0x12, 0x27, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x88, 0x02, 0x01, 0x12, 0xa6, 0x01, + 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, + 0x69, 0x65, 0x73, 0x56, 0x32, 0x12, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x12, 0x27, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2f, 0x73, - 0x74, 0x72, 0x65, 0x61, 0x6d, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0xb2, 0x01, 0x0a, 0x11, 0x57, - 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x72, 0x88, 0x02, 0x01, 0x12, 0xb9, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x79, + 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, + 0x73, 0x12, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, + 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x38, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, + 0x12, 0x2d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, + 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x88, + 0x02, 0x01, 0x12, 0x90, 0x01, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, + 0x69, 0x65, 0x73, 0x12, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x54, 0x43, 0x44, + 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x54, 0x43, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x12, 0x22, + 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x75, 0x74, 0x69, 0x65, 0x73, 0x2f, 0x70, + 0x74, 0x63, 0x88, 0x02, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x0a, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x6d, + 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x88, 0x02, 0x01, 0x12, 0x8e, 0x01, 0x0a, + 0x11, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x61, + 0x72, 0x74, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, 0x29, 0x2f, + 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0xb2, 0x01, + 0x0a, 0x11, 0x57, 0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2b, 0x12, - 0x29, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, - 0x97, 0x01, 0x0a, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x12, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x41, 0x63, 0x74, 0x69, 0x76, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x34, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x2b, 0x12, 0x29, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x63, 0x74, 0x69, + 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x88, 0x02, 0x01, + 0x30, 0x01, 0x12, 0x97, 0x01, 0x0a, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x02, 0x01, 0x12, 0x9b, 0x01, 0x0a, + 0x0f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, + 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x02, 0x01, 0x12, 0x9b, 0x01, 0x0a, 0x0f, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2d, 0x2e, + 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x02, 0x01, 0x12, 0xb5, 0x01, 0x0a, 0x17, 0x4d, + 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, + 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x88, 0x02, 0x01, 0x12, 0xb5, 0x01, 0x0a, 0x17, 0x4d, 0x75, 0x6c, 0x74, - 0x69, 0x70, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, - 0x69, 0x70, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x65, 0x74, 0x68, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, + 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x88, + 0x02, 0x01, 0x12, 0x8a, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x12, 0x20, 0x2f, 0x65, 0x74, 0x68, - 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, - 0x8a, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x23, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, + 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x02, 0x01, 0x12, + 0x9a, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1f, 0x12, 0x1d, 0x2f, 0x65, 0x74, 0x68, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, 0x22, 0x1d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x02, 0x01, 0x12, 0x9a, 0x01, 0x0a, - 0x12, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x69, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x1a, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, 0x22, 0x1d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x32, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x02, 0x01, 0x12, 0xa3, 0x01, 0x0a, 0x15, 0x50, 0x72, - 0x65, 0x70, 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x65, 0x70, - 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x3d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x3a, 0x01, 0x2a, 0x22, 0x2f, 0x2f, 0x65, 0x74, - 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, 0x62, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x88, 0x02, 0x01, 0x12, - 0xc2, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, - 0x65, 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, - 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, - 0x69, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x3a, 0x01, 0x2a, 0x22, - 0x30, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, - 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x75, 0x62, 0x5f, 0x6b, 0x65, - 0x79, 0x88, 0x02, 0x01, 0x12, 0x9b, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2d, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, - 0x74, 0x61, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x65, 0x74, 0x68, - 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, - 0x02, 0x01, 0x12, 0x92, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x25, 0x2e, + 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x88, 0x02, 0x01, 0x12, 0xa3, 0x01, 0x0a, + 0x15, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, + 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x3d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x34, 0x3a, 0x01, 0x2a, 0x22, 0x2f, + 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x65, 0x70, 0x61, 0x72, 0x65, 0x5f, + 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x88, + 0x02, 0x01, 0x12, 0xc2, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, + 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x12, 0x32, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, + 0x65, 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x65, 0x65, 0x52, 0x65, + 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x42, 0x79, 0x50, 0x75, 0x62, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x35, 0x3a, + 0x01, 0x2a, 0x22, 0x30, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x66, 0x65, 0x65, 0x5f, + 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x79, 0x5f, 0x70, 0x75, 0x62, + 0x5f, 0x6b, 0x65, 0x79, 0x88, 0x02, 0x01, 0x12, 0x9b, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2d, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x3a, 0x01, 0x2a, 0x22, - 0x23, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0xa7, 0x01, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, - 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, - 0x6e, 0x67, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, - 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, - 0x2a, 0x22, 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x88, 0x02, - 0x01, 0x12, 0xb5, 0x01, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x44, 0x61, 0x74, 0x61, 0x22, 0x2e, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, + 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0x92, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x22, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x31, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x28, 0x3a, + 0x01, 0x2a, 0x22, 0x23, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0xa7, 0x01, 0x0a, 0x19, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x1a, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, + 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, + 0x61, 0x88, 0x02, 0x01, 0x12, 0xb5, 0x01, 0x0a, 0x1d, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x26, 0x3a, 0x01, 0x2a, 0x22, 0x21, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, + 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x88, 0x02, 0x01, 0x12, 0xcb, 0x01, 0x0a, + 0x24, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x45, 0x6c, + 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, - 0x3a, 0x01, 0x2a, 0x22, 0x21, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x88, 0x02, 0x01, 0x12, 0xcb, 0x01, 0x0a, 0x24, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x45, 0x6c, 0x65, 0x63, 0x74, - 0x72, 0x61, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, - 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, - 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x3a, 0x01, 0x2a, 0x22, 0x29, 0x2f, 0x65, 0x74, 0x68, 0x2f, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x72, 0x61, 0x88, 0x02, 0x01, 0x12, 0xc1, 0x01, 0x0a, 0x23, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, - 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, - 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x26, 0x3a, 0x01, 0x2a, 0x22, 0x21, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x88, 0x02, 0x01, 0x12, 0xd7, 0x01, 0x0a, 0x2a, - 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, - 0x6f, 0x6f, 0x66, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x3a, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, - 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x37, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x2e, 0x3a, 0x01, 0x2a, 0x22, 0x29, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x72, 0x61, 0x88, 0x02, 0x01, 0x12, 0x91, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x45, 0x78, 0x69, 0x74, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, - 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x45, 0x78, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x3a, 0x01, 0x2a, 0x22, 0x29, 0x2f, 0x65, + 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x88, 0x02, 0x01, 0x12, 0xc1, 0x01, 0x0a, 0x23, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, + 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, + 0x6f, 0x66, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, + 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x3a, 0x01, 0x2a, 0x22, 0x21, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2f, 0x65, 0x78, 0x69, 0x74, 0x88, 0x02, 0x01, 0x12, 0xa4, 0x01, 0x0a, 0x19, 0x53, 0x75, - 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, - 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x37, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x72, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x88, 0x02, 0x01, 0x12, 0xd7, + 0x01, 0x0a, 0x2a, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, + 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x3a, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x37, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2e, 0x3a, 0x01, 0x2a, 0x22, 0x29, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x5f, 0x65, 0x6c, + 0x65, 0x63, 0x74, 0x72, 0x61, 0x88, 0x02, 0x01, 0x12, 0x91, 0x01, 0x0a, 0x0b, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x45, 0x78, 0x69, 0x74, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x69, 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x45, 0x78, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x65, 0x74, + 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x65, 0x78, 0x69, 0x74, 0x88, 0x02, 0x01, 0x12, 0xa4, 0x01, 0x0a, + 0x19, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x74, 0x65, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, 0x12, 0x37, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, 0x75, 0x62, 0x6e, + 0x65, 0x74, 0x73, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x36, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x2d, 0x3a, 0x01, 0x2a, 0x22, 0x28, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, + 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, + 0x88, 0x02, 0x01, 0x12, 0x9d, 0x01, 0x0a, 0x11, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x6f, 0x70, + 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, + 0x70, 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x2f, 0x64, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x67, 0x61, 0x6e, 0x67, 0x65, 0x72, + 0x88, 0x02, 0x01, 0x12, 0xa2, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x53, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x73, - 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, - 0x3a, 0x01, 0x2a, 0x22, 0x28, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x75, 0x62, - 0x6e, 0x65, 0x74, 0x2f, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x88, 0x02, 0x01, - 0x12, 0x9d, 0x01, 0x0a, 0x11, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x44, 0x6f, 0x70, 0x70, 0x65, 0x6c, - 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, - 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x6f, 0x70, 0x70, 0x65, - 0x6c, 0x47, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x2f, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x12, 0x24, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x64, 0x6f, 0x70, 0x70, 0x65, 0x6c, 0x67, 0x61, 0x6e, 0x67, 0x65, 0x72, 0x88, 0x02, 0x01, - 0x12, 0xa2, 0x01, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x6f, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x31, 0x12, 0x2f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x79, + 0x6e, 0x63, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x88, 0x02, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x11, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2b, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, + 0x74, 0x74, 0x65, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, + 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x6d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x88, 0x02, 0x01, 0x12, 0xb7, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x53, + 0x79, 0x6e, 0x63, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, - 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x6f, 0x6f, - 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x31, 0x12, 0x2f, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x6f, - 0x6f, 0x74, 0x88, 0x02, 0x01, 0x12, 0x8c, 0x01, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x53, 0x79, 0x6e, 0x63, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2b, 0x2e, 0x65, 0x74, + 0x63, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, + 0x65, 0x78, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, + 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x73, 0x75, 0x62, 0x63, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x02, + 0x01, 0x12, 0xc7, 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x37, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, + 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, - 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, - 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, 0x2f, 0x65, 0x74, - 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x88, 0x02, 0x01, 0x12, 0xb7, 0x01, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x53, 0x79, 0x6e, 0x63, - 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x12, 0x33, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x75, - 0x62, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, - 0x79, 0x6e, 0x63, 0x53, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x73, 0x75, 0x62, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x65, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x88, 0x02, 0x01, 0x12, 0xc7, - 0x01, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x37, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x74, 0x65, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x74, 0x65, 0x65, 0x43, 0x6f, - 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3c, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x33, 0x3a, 0x01, 0x2a, 0x22, 0x2e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x63, - 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x6e, 0x64, 0x5f, - 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x88, 0x02, 0x01, 0x12, 0xb2, 0x01, 0x0a, 0x20, 0x53, 0x75, 0x62, - 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x12, 0x31, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x3c, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x33, 0x3a, 0x01, 0x2a, 0x22, 0x2e, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, + 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x88, 0x02, 0x01, 0x12, 0xb2, 0x01, 0x0a, 0x20, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, 0x6f, 0x6f, 0x66, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x43, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3a, - 0x3a, 0x01, 0x2a, 0x22, 0x35, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x73, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x88, 0x02, 0x01, 0x12, 0x9e, 0x01, - 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x12, 0x29, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x12, 0x2b, 0x2f, 0x65, - 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0xa1, - 0x01, 0x0a, 0x12, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x41, - 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, - 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, - 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, + 0x12, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x43, + 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x6e, 0x64, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x43, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x3a, 0x3a, 0x01, 0x2a, 0x22, 0x35, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x88, 0x02, 0x01, - 0x30, 0x01, 0x12, 0xa1, 0x01, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x31, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, 0x22, 0x24, 0x2f, - 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0xb1, 0x01, 0x0a, 0x17, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x53, 0x75, 0x62, 0x6e, - 0x65, 0x74, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x73, 0x73, 0x69, 0x67, - 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x53, 0x75, 0x62, 0x6e, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x47, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3e, 0x3a, 0x01, 0x2a, 0x22, 0x39, 0x2f, 0x65, - 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x61, 0x73, 0x73, - 0x69, 0x67, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x74, 0x6f, - 0x5f, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x88, 0x02, 0x01, 0x12, 0xef, 0x01, 0x0a, 0x1f, 0x41, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, 0x41, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x12, 0x3d, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3e, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, - 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x4d, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x12, 0x42, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, - 0x5f, 0x73, 0x69, 0x67, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x88, 0x02, 0x01, 0x12, 0xd4, 0x01, 0x0a, - 0x1b, 0x47, 0x65, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x36, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, - 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x44, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x3b, 0x12, 0x39, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2f, 0x7b, 0x73, 0x6c, 0x6f, 0x74, 0x7d, - 0x88, 0x02, 0x01, 0x12, 0xb9, 0x01, 0x0a, 0x1f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, - 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x3c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x88, 0x02, 0x01, + 0x12, 0x9e, 0x01, 0x0a, 0x0b, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, 0x73, + 0x12, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, + 0x6c, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x6c, 0x6f, 0x74, 0x73, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2d, 0x12, + 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x73, 0x6c, 0x6f, 0x74, 0x73, 0x88, 0x02, 0x01, 0x30, + 0x01, 0x12, 0xa1, 0x01, 0x0a, 0x12, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x65, 0x74, 0x68, 0x2f, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x88, 0x02, 0x01, 0x30, 0x01, 0x12, 0xa1, 0x01, 0x0a, 0x1c, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, + 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x31, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x32, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x29, 0x3a, 0x01, 0x2a, + 0x22, 0x24, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0xb1, 0x01, 0x0a, 0x17, 0x41, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x53, + 0x75, 0x62, 0x6e, 0x65, 0x74, 0x12, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x73, + 0x73, 0x69, 0x67, 0x6e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x53, + 0x75, 0x62, 0x6e, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x47, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3e, 0x3a, 0x01, 0x2a, 0x22, + 0x39, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, + 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x75, 0x62, 0x6e, 0x65, 0x74, 0x88, 0x02, 0x01, 0x12, 0xef, 0x01, + 0x0a, 0x1f, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, + 0x6e, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, + 0x73, 0x12, 0x3d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x3e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x64, 0x53, 0x69, 0x67, 0x41, 0x6e, 0x64, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x69, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x4d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x44, 0x12, 0x42, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x2f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x73, 0x69, 0x67, 0x5f, 0x61, 0x6e, 0x64, 0x5f, 0x61, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x69, 0x74, 0x73, 0x88, 0x02, 0x01, 0x12, + 0xd4, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, + 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, - 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x40, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x3a, 0x01, 0x2a, 0x22, 0x32, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x44, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3b, 0x12, 0x39, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x88, 0x02, 0x01, 0x12, - 0xc1, 0x01, 0x0a, 0x16, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x2e, 0x65, 0x74, 0x68, + 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x2f, 0x7b, 0x73, 0x6c, + 0x6f, 0x74, 0x7d, 0x88, 0x02, 0x01, 0x12, 0xb9, 0x01, 0x0a, 0x1f, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x73, 0x68, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x3c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x22, - 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x12, 0x37, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x7b, 0x73, 0x6c, 0x6f, 0x74, 0x7d, - 0x88, 0x02, 0x01, 0x12, 0x9f, 0x01, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, - 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0xb4, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x50, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x61, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x3a, 0x01, 0x2a, 0x22, 0x2c, 0x2f, 0x65, 0x74, + 0x22, 0x40, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x37, 0x3a, 0x01, 0x2a, 0x22, 0x32, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, 0xa5, 0x01, 0x0a, - 0x18, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, - 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, - 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x39, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, - 0x73, 0x88, 0x02, 0x01, 0x12, 0xa8, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x69, 0x64, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x69, 0x64, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x3a, 0x01, 0x2a, 0x22, 0x2d, 0x2f, - 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x88, 0x02, 0x01, 0x42, - 0x92, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0e, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, - 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, - 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x15, 0x45, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x88, + 0x02, 0x01, 0x12, 0xc1, 0x01, 0x0a, 0x16, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, + 0x74, 0x61, 0x22, 0x42, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x39, 0x12, 0x37, 0x2f, 0x65, 0x74, 0x68, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x7b, 0x73, 0x6c, + 0x6f, 0x74, 0x7d, 0x88, 0x02, 0x01, 0x12, 0x9f, 0x01, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x6d, 0x69, + 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x39, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x02, 0x01, 0x12, 0xb4, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, + 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x3d, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x22, 0x3a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x31, 0x3a, 0x01, 0x2a, 0x22, 0x2c, + 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, + 0xa8, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x42, 0x69, 0x64, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x42, 0x69, 0x64, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3b, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x3a, 0x01, 0x2a, 0x22, 0x2d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x88, 0x02, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x19, 0x6f, + 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, + 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5509,7 +5499,7 @@ var file_proto_prysm_v1alpha1_validator_proto_goTypes = []any{ (*SignedBeaconBlockElectra)(nil), // 73: ethereum.eth.v1alpha1.SignedBeaconBlockElectra (*SignedBeaconBlockFulu)(nil), // 74: ethereum.eth.v1alpha1.SignedBeaconBlockFulu (*wrapperspb.UInt64Value)(nil), // 75: google.protobuf.UInt64Value - (*SignedRequestAuthV1)(nil), // 76: ethereum.eth.v1alpha1.SignedRequestAuthV1 + (*BuilderPreferenceV1)(nil), // 76: ethereum.eth.v1alpha1.BuilderPreferenceV1 (*AggregateAttestationAndProof)(nil), // 77: ethereum.eth.v1alpha1.AggregateAttestationAndProof (*AggregateAttestationAndProofElectra)(nil), // 78: ethereum.eth.v1alpha1.AggregateAttestationAndProofElectra (*SignedAggregateAttestationAndProof)(nil), // 79: ethereum.eth.v1alpha1.SignedAggregateAttestationAndProof @@ -5526,12 +5516,11 @@ var file_proto_prysm_v1alpha1_validator_proto_goTypes = []any{ (*GenericSignedExecutionPayloadEnvelope)(nil), // 90: ethereum.eth.v1alpha1.GenericSignedExecutionPayloadEnvelope (*PayloadAttestationMessage)(nil), // 91: ethereum.eth.v1alpha1.PayloadAttestationMessage (*SubmitSignedProposerPreferencesRequest)(nil), // 92: ethereum.eth.v1alpha1.SubmitSignedProposerPreferencesRequest - (*SubmitBuilderPreferencesRequest)(nil), // 93: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest - (*SignedExecutionPayloadBid)(nil), // 94: ethereum.eth.v1alpha1.SignedExecutionPayloadBid - (*GenericBeaconBlock)(nil), // 95: ethereum.eth.v1alpha1.GenericBeaconBlock - (*AttestationData)(nil), // 96: ethereum.eth.v1alpha1.AttestationData - (*SyncCommitteeContribution)(nil), // 97: ethereum.eth.v1alpha1.SyncCommitteeContribution - (*PayloadAttestationData)(nil), // 98: ethereum.eth.v1alpha1.PayloadAttestationData + (*SignedExecutionPayloadBid)(nil), // 93: ethereum.eth.v1alpha1.SignedExecutionPayloadBid + (*GenericBeaconBlock)(nil), // 94: ethereum.eth.v1alpha1.GenericBeaconBlock + (*AttestationData)(nil), // 95: ethereum.eth.v1alpha1.AttestationData + (*SyncCommitteeContribution)(nil), // 96: ethereum.eth.v1alpha1.SyncCommitteeContribution + (*PayloadAttestationData)(nil), // 97: ethereum.eth.v1alpha1.PayloadAttestationData } var file_proto_prysm_v1alpha1_validator_proto_depIdxs = []int32{ 68, // 0: ethereum.eth.v1alpha1.StreamBlocksResponse.phase0_block:type_name -> ethereum.eth.v1alpha1.SignedBeaconBlock @@ -5549,7 +5538,7 @@ var file_proto_prysm_v1alpha1_validator_proto_depIdxs = []int32{ 64, // 12: ethereum.eth.v1alpha1.DutiesV2Response.current_epoch_duties:type_name -> ethereum.eth.v1alpha1.DutiesV2Response.Duty 64, // 13: ethereum.eth.v1alpha1.DutiesV2Response.next_epoch_duties:type_name -> ethereum.eth.v1alpha1.DutiesV2Response.Duty 75, // 14: ethereum.eth.v1alpha1.BlockRequest.builder_boost_factor:type_name -> google.protobuf.UInt64Value - 76, // 15: ethereum.eth.v1alpha1.BlockRequest.builder_request_auths:type_name -> ethereum.eth.v1alpha1.SignedRequestAuthV1 + 76, // 15: ethereum.eth.v1alpha1.BlockRequest.builder_preferences:type_name -> ethereum.eth.v1alpha1.BuilderPreferenceV1 77, // 16: ethereum.eth.v1alpha1.AggregateSelectionResponse.aggregate_and_proof:type_name -> ethereum.eth.v1alpha1.AggregateAttestationAndProof 78, // 17: ethereum.eth.v1alpha1.AggregateSelectionElectraResponse.aggregate_and_proof:type_name -> ethereum.eth.v1alpha1.AggregateAttestationAndProofElectra 79, // 18: ethereum.eth.v1alpha1.SignedAggregateSubmitRequest.signed_aggregate_and_proof:type_name -> ethereum.eth.v1alpha1.SignedAggregateAttestationAndProof @@ -5608,53 +5597,51 @@ var file_proto_prysm_v1alpha1_validator_proto_depIdxs = []int32{ 61, // 71: ethereum.eth.v1alpha1.BeaconNodeValidator.PayloadAttestationData:input_type -> ethereum.eth.v1alpha1.PayloadAttestationDataRequest 91, // 72: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitPayloadAttestation:input_type -> ethereum.eth.v1alpha1.PayloadAttestationMessage 92, // 73: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedProposerPreferences:input_type -> ethereum.eth.v1alpha1.SubmitSignedProposerPreferencesRequest - 93, // 74: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitBuilderPreferences:input_type -> ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest - 94, // 75: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:input_type -> ethereum.eth.v1alpha1.SignedExecutionPayloadBid - 20, // 76: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDuties:output_type -> ethereum.eth.v1alpha1.DutiesResponse - 21, // 77: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDutiesV2:output_type -> ethereum.eth.v1alpha1.DutiesV2Response - 48, // 78: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttesterDuties:output_type -> ethereum.eth.v1alpha1.AttesterDutiesResponse - 51, // 79: ethereum.eth.v1alpha1.BeaconNodeValidator.GetProposerDutiesV2:output_type -> ethereum.eth.v1alpha1.ProposerDutiesResponse - 54, // 80: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeDuties:output_type -> ethereum.eth.v1alpha1.SyncCommitteeDutiesResponse - 57, // 81: ethereum.eth.v1alpha1.BeaconNodeValidator.GetPTCDuties:output_type -> ethereum.eth.v1alpha1.PTCDutiesResponse - 8, // 82: ethereum.eth.v1alpha1.BeaconNodeValidator.DomainData:output_type -> ethereum.eth.v1alpha1.DomainResponse - 11, // 83: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForChainStart:output_type -> ethereum.eth.v1alpha1.ChainStartResponse - 10, // 84: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForActivation:output_type -> ethereum.eth.v1alpha1.ValidatorActivationResponse - 14, // 85: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorIndex:output_type -> ethereum.eth.v1alpha1.ValidatorIndexResponse - 16, // 86: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorStatus:output_type -> ethereum.eth.v1alpha1.ValidatorStatusResponse - 18, // 87: ethereum.eth.v1alpha1.BeaconNodeValidator.MultipleValidatorStatus:output_type -> ethereum.eth.v1alpha1.MultipleValidatorStatusResponse - 95, // 88: ethereum.eth.v1alpha1.BeaconNodeValidator.GetBeaconBlock:output_type -> ethereum.eth.v1alpha1.GenericBeaconBlock - 23, // 89: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeBeaconBlock:output_type -> ethereum.eth.v1alpha1.ProposeResponse - 83, // 90: ethereum.eth.v1alpha1.BeaconNodeValidator.PrepareBeaconProposer:output_type -> google.protobuf.Empty - 43, // 91: ethereum.eth.v1alpha1.BeaconNodeValidator.GetFeeRecipientByPubKey:output_type -> ethereum.eth.v1alpha1.FeeRecipientByPubKeyResponse - 96, // 92: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttestationData:output_type -> ethereum.eth.v1alpha1.AttestationData - 26, // 93: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestation:output_type -> ethereum.eth.v1alpha1.AttestResponse - 26, // 94: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestationElectra:output_type -> ethereum.eth.v1alpha1.AttestResponse - 28, // 95: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.AggregateSelectionResponse - 29, // 96: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.AggregateSelectionElectraResponse - 32, // 97: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse - 32, // 98: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse - 24, // 99: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeExit:output_type -> ethereum.eth.v1alpha1.ProposeExitResponse - 83, // 100: ethereum.eth.v1alpha1.BeaconNodeValidator.SubscribeCommitteeSubnets:output_type -> google.protobuf.Empty - 38, // 101: ethereum.eth.v1alpha1.BeaconNodeValidator.CheckDoppelGanger:output_type -> ethereum.eth.v1alpha1.DoppelGangerResponse - 1, // 102: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncMessageBlockRoot:output_type -> ethereum.eth.v1alpha1.SyncMessageBlockRootResponse - 83, // 103: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSyncMessage:output_type -> google.protobuf.Empty - 4, // 104: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncSubcommitteeIndex:output_type -> ethereum.eth.v1alpha1.SyncSubcommitteeIndexResponse - 97, // 105: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeContribution:output_type -> ethereum.eth.v1alpha1.SyncCommitteeContribution - 83, // 106: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedContributionAndProof:output_type -> google.protobuf.Empty - 5, // 107: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamSlots:output_type -> ethereum.eth.v1alpha1.StreamSlotsResponse - 6, // 108: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamBlocksAltair:output_type -> ethereum.eth.v1alpha1.StreamBlocksResponse - 83, // 109: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitValidatorRegistrations:output_type -> google.protobuf.Empty - 83, // 110: ethereum.eth.v1alpha1.BeaconNodeValidator.AssignValidatorToSubnet:output_type -> google.protobuf.Empty - 46, // 111: ethereum.eth.v1alpha1.BeaconNodeValidator.AggregatedSigAndAggregationBits:output_type -> ethereum.eth.v1alpha1.AggregatedSigAndAggregationBitsResponse - 60, // 112: ethereum.eth.v1alpha1.BeaconNodeValidator.GetExecutionPayloadEnvelope:output_type -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelopeResponse - 83, // 113: ethereum.eth.v1alpha1.BeaconNodeValidator.PublishExecutionPayloadEnvelope:output_type -> google.protobuf.Empty - 98, // 114: ethereum.eth.v1alpha1.BeaconNodeValidator.PayloadAttestationData:output_type -> ethereum.eth.v1alpha1.PayloadAttestationData - 83, // 115: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitPayloadAttestation:output_type -> google.protobuf.Empty - 83, // 116: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedProposerPreferences:output_type -> google.protobuf.Empty - 83, // 117: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitBuilderPreferences:output_type -> google.protobuf.Empty - 83, // 118: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:output_type -> google.protobuf.Empty - 76, // [76:119] is the sub-list for method output_type - 33, // [33:76] is the sub-list for method input_type + 93, // 74: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:input_type -> ethereum.eth.v1alpha1.SignedExecutionPayloadBid + 20, // 75: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDuties:output_type -> ethereum.eth.v1alpha1.DutiesResponse + 21, // 76: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDutiesV2:output_type -> ethereum.eth.v1alpha1.DutiesV2Response + 48, // 77: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttesterDuties:output_type -> ethereum.eth.v1alpha1.AttesterDutiesResponse + 51, // 78: ethereum.eth.v1alpha1.BeaconNodeValidator.GetProposerDutiesV2:output_type -> ethereum.eth.v1alpha1.ProposerDutiesResponse + 54, // 79: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeDuties:output_type -> ethereum.eth.v1alpha1.SyncCommitteeDutiesResponse + 57, // 80: ethereum.eth.v1alpha1.BeaconNodeValidator.GetPTCDuties:output_type -> ethereum.eth.v1alpha1.PTCDutiesResponse + 8, // 81: ethereum.eth.v1alpha1.BeaconNodeValidator.DomainData:output_type -> ethereum.eth.v1alpha1.DomainResponse + 11, // 82: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForChainStart:output_type -> ethereum.eth.v1alpha1.ChainStartResponse + 10, // 83: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForActivation:output_type -> ethereum.eth.v1alpha1.ValidatorActivationResponse + 14, // 84: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorIndex:output_type -> ethereum.eth.v1alpha1.ValidatorIndexResponse + 16, // 85: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorStatus:output_type -> ethereum.eth.v1alpha1.ValidatorStatusResponse + 18, // 86: ethereum.eth.v1alpha1.BeaconNodeValidator.MultipleValidatorStatus:output_type -> ethereum.eth.v1alpha1.MultipleValidatorStatusResponse + 94, // 87: ethereum.eth.v1alpha1.BeaconNodeValidator.GetBeaconBlock:output_type -> ethereum.eth.v1alpha1.GenericBeaconBlock + 23, // 88: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeBeaconBlock:output_type -> ethereum.eth.v1alpha1.ProposeResponse + 83, // 89: ethereum.eth.v1alpha1.BeaconNodeValidator.PrepareBeaconProposer:output_type -> google.protobuf.Empty + 43, // 90: ethereum.eth.v1alpha1.BeaconNodeValidator.GetFeeRecipientByPubKey:output_type -> ethereum.eth.v1alpha1.FeeRecipientByPubKeyResponse + 95, // 91: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttestationData:output_type -> ethereum.eth.v1alpha1.AttestationData + 26, // 92: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestation:output_type -> ethereum.eth.v1alpha1.AttestResponse + 26, // 93: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestationElectra:output_type -> ethereum.eth.v1alpha1.AttestResponse + 28, // 94: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.AggregateSelectionResponse + 29, // 95: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.AggregateSelectionElectraResponse + 32, // 96: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse + 32, // 97: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse + 24, // 98: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeExit:output_type -> ethereum.eth.v1alpha1.ProposeExitResponse + 83, // 99: ethereum.eth.v1alpha1.BeaconNodeValidator.SubscribeCommitteeSubnets:output_type -> google.protobuf.Empty + 38, // 100: ethereum.eth.v1alpha1.BeaconNodeValidator.CheckDoppelGanger:output_type -> ethereum.eth.v1alpha1.DoppelGangerResponse + 1, // 101: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncMessageBlockRoot:output_type -> ethereum.eth.v1alpha1.SyncMessageBlockRootResponse + 83, // 102: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSyncMessage:output_type -> google.protobuf.Empty + 4, // 103: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncSubcommitteeIndex:output_type -> ethereum.eth.v1alpha1.SyncSubcommitteeIndexResponse + 96, // 104: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeContribution:output_type -> ethereum.eth.v1alpha1.SyncCommitteeContribution + 83, // 105: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedContributionAndProof:output_type -> google.protobuf.Empty + 5, // 106: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamSlots:output_type -> ethereum.eth.v1alpha1.StreamSlotsResponse + 6, // 107: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamBlocksAltair:output_type -> ethereum.eth.v1alpha1.StreamBlocksResponse + 83, // 108: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitValidatorRegistrations:output_type -> google.protobuf.Empty + 83, // 109: ethereum.eth.v1alpha1.BeaconNodeValidator.AssignValidatorToSubnet:output_type -> google.protobuf.Empty + 46, // 110: ethereum.eth.v1alpha1.BeaconNodeValidator.AggregatedSigAndAggregationBits:output_type -> ethereum.eth.v1alpha1.AggregatedSigAndAggregationBitsResponse + 60, // 111: ethereum.eth.v1alpha1.BeaconNodeValidator.GetExecutionPayloadEnvelope:output_type -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelopeResponse + 83, // 112: ethereum.eth.v1alpha1.BeaconNodeValidator.PublishExecutionPayloadEnvelope:output_type -> google.protobuf.Empty + 97, // 113: ethereum.eth.v1alpha1.BeaconNodeValidator.PayloadAttestationData:output_type -> ethereum.eth.v1alpha1.PayloadAttestationData + 83, // 114: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitPayloadAttestation:output_type -> google.protobuf.Empty + 83, // 115: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedProposerPreferences:output_type -> google.protobuf.Empty + 83, // 116: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:output_type -> google.protobuf.Empty + 75, // [75:117] is the sub-list for method output_type + 33, // [33:75] is the sub-list for method input_type 33, // [33:33] is the sub-list for extension type_name 33, // [33:33] is the sub-list for extension extendee 0, // [0:33] is the sub-list for field type_name @@ -5796,8 +5783,6 @@ type BeaconNodeValidatorClient interface { // Deprecated: Do not use. SubmitSignedProposerPreferences(ctx context.Context, in *SubmitSignedProposerPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // Deprecated: Do not use. - SubmitBuilderPreferences(ctx context.Context, in *SubmitBuilderPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) - // Deprecated: Do not use. SubmitSignedExecutionPayloadBid(ctx context.Context, in *SignedExecutionPayloadBid, opts ...grpc.CallOption) (*emptypb.Empty, error) } @@ -6311,16 +6296,6 @@ func (c *beaconNodeValidatorClient) SubmitSignedProposerPreferences(ctx context. return out, nil } -// Deprecated: Do not use. -func (c *beaconNodeValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *SubmitBuilderPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.BeaconNodeValidator/SubmitBuilderPreferences", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - // Deprecated: Do not use. func (c *beaconNodeValidatorClient) SubmitSignedExecutionPayloadBid(ctx context.Context, in *SignedExecutionPayloadBid, opts ...grpc.CallOption) (*emptypb.Empty, error) { out := new(emptypb.Empty) @@ -6416,8 +6391,6 @@ type BeaconNodeValidatorServer interface { // Deprecated: Do not use. SubmitSignedProposerPreferences(context.Context, *SubmitSignedProposerPreferencesRequest) (*emptypb.Empty, error) // Deprecated: Do not use. - SubmitBuilderPreferences(context.Context, *SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) - // Deprecated: Do not use. SubmitSignedExecutionPayloadBid(context.Context, *SignedExecutionPayloadBid) (*emptypb.Empty, error) } @@ -6548,9 +6521,6 @@ func (*UnimplementedBeaconNodeValidatorServer) SubmitPayloadAttestation(context. func (*UnimplementedBeaconNodeValidatorServer) SubmitSignedProposerPreferences(context.Context, *SubmitSignedProposerPreferencesRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitSignedProposerPreferences not implemented") } -func (*UnimplementedBeaconNodeValidatorServer) SubmitBuilderPreferences(context.Context, *SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method SubmitBuilderPreferences not implemented") -} func (*UnimplementedBeaconNodeValidatorServer) SubmitSignedExecutionPayloadBid(context.Context, *SignedExecutionPayloadBid) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitSignedExecutionPayloadBid not implemented") } @@ -7309,24 +7279,6 @@ func _BeaconNodeValidator_SubmitSignedProposerPreferences_Handler(srv interface{ return interceptor(ctx, in, info, handler) } -func _BeaconNodeValidator_SubmitBuilderPreferences_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SubmitBuilderPreferencesRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BeaconNodeValidatorServer).SubmitBuilderPreferences(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/ethereum.eth.v1alpha1.BeaconNodeValidator/SubmitBuilderPreferences", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BeaconNodeValidatorServer).SubmitBuilderPreferences(ctx, req.(*SubmitBuilderPreferencesRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _BeaconNodeValidator_SubmitSignedExecutionPayloadBid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SignedExecutionPayloadBid) if err := dec(in); err != nil { @@ -7497,10 +7449,6 @@ var _BeaconNodeValidator_serviceDesc = grpc.ServiceDesc{ MethodName: "SubmitSignedProposerPreferences", Handler: _BeaconNodeValidator_SubmitSignedProposerPreferences_Handler, }, - { - MethodName: "SubmitBuilderPreferences", - Handler: _BeaconNodeValidator_SubmitBuilderPreferences_Handler, - }, { MethodName: "SubmitSignedExecutionPayloadBid", Handler: _BeaconNodeValidator_SubmitSignedExecutionPayloadBid_Handler, diff --git a/proto/prysm/v1alpha1/validator.proto b/proto/prysm/v1alpha1/validator.proto index 353c490a6a3f..59bd223a6132 100644 --- a/proto/prysm/v1alpha1/validator.proto +++ b/proto/prysm/v1alpha1/validator.proto @@ -542,17 +542,6 @@ service BeaconNodeValidator { }; } - // SubmitBuilderPreferences forwards a proposer's builder preferences to the - // configured builder ahead of a future proposal slot. - rpc SubmitBuilderPreferences(SubmitBuilderPreferencesRequest) - returns (google.protobuf.Empty) { - option deprecated = true; - option (google.api.http) = { - post : "/eth/v1alpha1/validator/builder_preferences" - body : "*" - }; - } - // SubmitSignedExecutionPayloadBid broadcasts a signed execution payload bid // to the P2P gossip network. rpc SubmitSignedExecutionPayloadBid(SignedExecutionPayloadBid) @@ -979,9 +968,12 @@ message BlockRequest { // populated state root. bool eager_payload_state_root = 6; - // Signed builder request auths for this slot, one per configured builder. - // The beacon node forwards the auth matching its builder when requesting a bid. - repeated SignedRequestAuthV1 builder_request_auths = 7; + reserved 7; // was builder_request_auths; folded into builder_preferences. + + // Per-builder preferences for this proposal (JIT model). Each entry carries the + // signed request/auth the beacon node forwards to its builder, plus the + // proposer-local min_bid and builder_boost_factor used for bid selection. + repeated BuilderPreferenceV1 builder_preferences = 8; } message ProposeResponse { diff --git a/testing/mock/beacon_validator_client_mock.go b/testing/mock/beacon_validator_client_mock.go index 9368faf4bbfe..4573c0855caa 100644 --- a/testing/mock/beacon_validator_client_mock.go +++ b/testing/mock/beacon_validator_client_mock.go @@ -624,26 +624,6 @@ func (mr *MockBeaconNodeValidatorClientMockRecorder) SubmitAggregateSelectionPro return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProofElectra", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).SubmitAggregateSelectionProofElectra), varargs...) } -// SubmitBuilderPreferences mocks base method. -func (m *MockBeaconNodeValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *eth.SubmitBuilderPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { - m.ctrl.T.Helper() - varargs := []any{ctx, in} - for _, a := range opts { - varargs = append(varargs, a) - } - ret := m.ctrl.Call(m, "SubmitBuilderPreferences", varargs...) - ret0, _ := ret[0].(*emptypb.Empty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SubmitBuilderPreferences indicates an expected call of SubmitBuilderPreferences. -func (mr *MockBeaconNodeValidatorClientMockRecorder) SubmitBuilderPreferences(ctx, in any, opts ...any) *gomock.Call { - mr.mock.ctrl.T.Helper() - varargs := append([]any{ctx, in}, opts...) - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBuilderPreferences", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).SubmitBuilderPreferences), varargs...) -} - // SubmitPayloadAttestation mocks base method. func (m *MockBeaconNodeValidatorClient) SubmitPayloadAttestation(ctx context.Context, in *eth.PayloadAttestationMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { m.ctrl.T.Helper() diff --git a/testing/mock/beacon_validator_server_mock.go b/testing/mock/beacon_validator_server_mock.go index 8863fc6ba72c..02cc8a4bd796 100644 --- a/testing/mock/beacon_validator_server_mock.go +++ b/testing/mock/beacon_validator_server_mock.go @@ -476,21 +476,6 @@ func (mr *MockBeaconNodeValidatorServerMockRecorder) SubmitAggregateSelectionPro return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProofElectra", reflect.TypeOf((*MockBeaconNodeValidatorServer)(nil).SubmitAggregateSelectionProofElectra), arg0, arg1) } -// SubmitBuilderPreferences mocks base method. -func (m *MockBeaconNodeValidatorServer) SubmitBuilderPreferences(arg0 context.Context, arg1 *eth.SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBuilderPreferences", arg0, arg1) - ret0, _ := ret[0].(*emptypb.Empty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SubmitBuilderPreferences indicates an expected call of SubmitBuilderPreferences. -func (mr *MockBeaconNodeValidatorServerMockRecorder) SubmitBuilderPreferences(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBuilderPreferences", reflect.TypeOf((*MockBeaconNodeValidatorServer)(nil).SubmitBuilderPreferences), arg0, arg1) -} - // SubmitPayloadAttestation mocks base method. func (m *MockBeaconNodeValidatorServer) SubmitPayloadAttestation(arg0 context.Context, arg1 *eth.PayloadAttestationMessage) (*emptypb.Empty, error) { m.ctrl.T.Helper() diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index eb2b55a62280..75c32c4e7fc0 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -443,21 +443,6 @@ func (mr *MockValidatorClientMockRecorder) SubmitAggregateSelectionProofElectra( return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProofElectra", reflect.TypeOf((*MockValidatorClient)(nil).SubmitAggregateSelectionProofElectra), ctx, in, arg2, arg3) } -// SubmitBuilderPreferences mocks base method. -func (m *MockValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *eth.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "SubmitBuilderPreferences", ctx, in) - ret0, _ := ret[0].(*empty.Empty) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// SubmitBuilderPreferences indicates an expected call of SubmitBuilderPreferences. -func (mr *MockValidatorClientMockRecorder) SubmitBuilderPreferences(ctx, in any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBuilderPreferences", reflect.TypeOf((*MockValidatorClient)(nil).SubmitBuilderPreferences), ctx, in) -} - // SubmitPayloadAttestation mocks base method. func (m *MockValidatorClient) SubmitPayloadAttestation(ctx context.Context, in *eth.PayloadAttestationMessage) (*empty.Empty, error) { m.ctrl.T.Helper() diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index 4460d0f87a82..a3b8d5625b92 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -52,6 +52,7 @@ go_library( "//consensus-types/blocks:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", + "//consensus-types/validator:go_default_library", "//container/slice:go_default_library", "//crypto/bls:go_default_library", "//crypto/hash:go_default_library", @@ -102,6 +103,7 @@ go_library( "@org_golang_google_grpc//status:go_default_library", "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//types/known/emptypb:go_default_library", + "@org_golang_google_protobuf//types/known/wrapperspb:go_default_library", "@org_golang_x_sync//errgroup:go_default_library", "@org_golang_x_sync//singleflight:go_default_library", ], diff --git a/validator/client/beacon-api/BUILD.bazel b/validator/client/beacon-api/BUILD.bazel index 243addc4fc74..dc7a40edf71c 100644 --- a/validator/client/beacon-api/BUILD.bazel +++ b/validator/client/beacon-api/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "beacon_block_json_helpers.go", "beacon_block_proto_helpers.go", "beacon_committee_selections.go", + "builder_preferences.go", "domain_data.go", "doppelganger.go", "duties.go", @@ -70,6 +71,7 @@ go_library( "@com_github_sirupsen_logrus//:go_default_library", "@org_golang_google_grpc//:go_default_library", "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", + "@org_golang_google_protobuf//types/known/wrapperspb:go_default_library", "@org_golang_x_sync//errgroup:go_default_library", ], ) diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index 7dd79c557e76..ae65443225d2 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -135,7 +135,7 @@ func (c *beaconApiValidatorClient) BeaconBlock(ctx context.Context, in *ethpb.Bl defer span.End() return wrapInMetrics[*ethpb.GenericBeaconBlock]("BeaconBlock", func() (*ethpb.GenericBeaconBlock, error) { - return c.beaconBlock(ctx, in.Slot, in.RandaoReveal, in.Graffiti) + return c.beaconBlock(ctx, in.Slot, in.RandaoReveal, in.Graffiti, in.BuilderBoostFactor, in.BuilderPreferences) }) } @@ -300,12 +300,6 @@ func (c *beaconApiValidatorClient) SubmitSignedProposerPreferences(ctx context.C }) } -// TODO(gloas): Wire up actual REST call to POST /eth/v1alpha1/validator/builder_preferences -func (c *beaconApiValidatorClient) SubmitBuilderPreferences(_ context.Context, _ *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { - log.Debug("SubmitBuilderPreferences not yet implemented for beacon API client, skipping") - return new(empty.Empty), nil -} - // TODO(gloas): Wire up actual REST call to POST /eth/v2/beacon/execution_payload/bid func (c *beaconApiValidatorClient) SubmitSignedExecutionPayloadBid(_ context.Context, _ *ethpb.SignedExecutionPayloadBid) (*empty.Empty, error) { log.Debug("SubmitSignedExecutionPayloadBid not yet implemented for beacon API client, skipping") diff --git a/validator/client/beacon-api/builder_preferences.go b/validator/client/beacon-api/builder_preferences.go new file mode 100644 index 000000000000..88b445407c67 --- /dev/null +++ b/validator/client/beacon-api/builder_preferences.go @@ -0,0 +1,59 @@ +package beacon_api + +import ( + "encoding/json" + "strconv" + + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// JIT builder-preferences POST body for produceBlockV4 (beacon-APIs #625). +type builderPreferencesBody struct { + BuilderPreferences []*builderPreferenceReq `json:"builder_preferences"` +} + +type builderPreferenceReq struct { + SignedRequestAuth *signedRequestAuthReq `json:"signed_request_auth"` + MaxExecutionPayment string `json:"max_execution_payment"` + MinBid string `json:"min_bid,omitempty"` + BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` +} + +type signedRequestAuthReq struct { + Message *requestAuthReq `json:"message"` + Signature string `json:"signature"` +} + +type requestAuthReq struct { + Data string `json:"data"` + Slot string `json:"slot"` +} + +// marshalBuilderPreferences encodes inline builder preferences into the #625 JSON body. +func marshalBuilderPreferences(prefs []*ethpb.BuilderPreferenceV1) ([]byte, error) { + body := &builderPreferencesBody{BuilderPreferences: make([]*builderPreferenceReq, 0, len(prefs))} + for _, p := range prefs { + if p == nil || p.Request == nil || p.Request.Auth == nil || p.Request.Auth.Message == nil { + continue + } + req := &builderPreferenceReq{ + MaxExecutionPayment: strconv.FormatUint(uint64(p.Request.Preferences.GetMaxExecutionPayment()), 10), + SignedRequestAuth: &signedRequestAuthReq{ + Message: &requestAuthReq{ + Data: hexutil.Encode(p.Request.Auth.Message.Data), + Slot: strconv.FormatUint(uint64(p.Request.Auth.Message.Slot), 10), + }, + Signature: hexutil.Encode(p.Request.Auth.Signature), + }, + } + if p.MinBid != nil { + req.MinBid = strconv.FormatUint(uint64(p.GetMinBid()), 10) + } + if p.BuilderBoostFactor != nil { + req.BuilderBoostFactor = strconv.FormatUint(p.GetBuilderBoostFactor(), 10) + } + body.BuilderPreferences = append(body.BuilderPreferences, req) + } + return json.Marshal(body) +} diff --git a/validator/client/beacon-api/get_beacon_block.go b/validator/client/beacon-api/get_beacon_block.go index f134358902c9..fe10ece098ef 100644 --- a/validator/client/beacon-api/get_beacon_block.go +++ b/validator/client/beacon-api/get_beacon_block.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "fmt" + "net/http" neturl "net/url" "strconv" "strings" @@ -19,9 +20,10 @@ import ( "github.com/OffchainLabs/prysm/v7/time/slots" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" + "google.golang.org/protobuf/types/known/wrapperspb" ) -func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primitives.Slot, randaoReveal, graffiti []byte) (*ethpb.GenericBeaconBlock, error) { +func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primitives.Slot, randaoReveal, graffiti []byte, builderBoostFactor *wrapperspb.UInt64Value, builderPreferences []*ethpb.BuilderPreferenceV1) (*ethpb.GenericBeaconBlock, error) { queryParams := neturl.Values{} queryParams.Add("randao_reveal", hexutil.Encode(randaoReveal)) if len(graffiti) > 0 { @@ -29,9 +31,12 @@ func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primiti } if slots.ToEpoch(slot) >= params.BeaconConfig().GloasForkEpoch { - return c.beaconBlockV4(ctx, slot, queryParams) + return c.beaconBlockV4(ctx, slot, queryParams, builderPreferences) } + if builderBoostFactor != nil { + queryParams.Add("builder_boost_factor", strconv.FormatUint(builderBoostFactor.Value, 10)) + } queryUrl := apiutil.BuildURL(fmt.Sprintf("/eth/v3/validator/blocks/%d", slot), queryParams) data, header, err := c.handler.GetSSZ(ctx, queryUrl) if err != nil { @@ -62,10 +67,24 @@ func (c *beaconApiValidatorClient) beaconBlock(ctx context.Context, slot primiti } } -func (c *beaconApiValidatorClient) beaconBlockV4(ctx context.Context, slot primitives.Slot, queryParams neturl.Values) (*ethpb.GenericBeaconBlock, error) { +func (c *beaconApiValidatorClient) beaconBlockV4(ctx context.Context, slot primitives.Slot, queryParams neturl.Values, builderPreferences []*ethpb.BuilderPreferenceV1) (*ethpb.GenericBeaconBlock, error) { queryParams.Set("include_payload", strconv.FormatBool(c.stateless)) queryUrl := apiutil.BuildURL(fmt.Sprintf("/eth/v4/validator/blocks/%d", slot), queryParams) - data, header, err := c.handler.GetSSZ(ctx, queryUrl) + + var data []byte + var header http.Header + var err error + if len(builderPreferences) > 0 { + // JIT (beacon-APIs #625): POST the per-builder preferences inline. + body, mErr := marshalBuilderPreferences(builderPreferences) + if mErr != nil { + return nil, errors.Wrap(mErr, "could not marshal builder preferences") + } + headers := map[string]string{"Content-Type": api.JsonMediaType} + data, header, err = c.handler.PostSSZ(ctx, queryUrl, headers, bytes.NewBuffer(body)) + } else { + data, header, err = c.handler.GetSSZ(ctx, queryUrl) + } if err != nil { return nil, errors.Wrap(err, "could not get v4 beacon block") } diff --git a/validator/client/beacon-api/get_beacon_block_test.go b/validator/client/beacon-api/get_beacon_block_test.go index 55265a9854a8..fe401e1ddbb3 100644 --- a/validator/client/beacon-api/get_beacon_block_test.go +++ b/validator/client/beacon-api/get_beacon_block_test.go @@ -39,7 +39,7 @@ func TestGetBeaconBlock_RequestFailed(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - _, err := validatorClient.beaconBlock(ctx, 1, []byte{1}, []byte{2}) + _, err := validatorClient.beaconBlock(ctx, 1, []byte{1}, []byte{2}, nil, nil) assert.ErrorContains(t, "foo error", err) } @@ -163,7 +163,7 @@ func TestGetBeaconBlock_Error(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - _, err = validatorClient.beaconBlock(ctx, 1, []byte{1}, []byte{2}) + _, err = validatorClient.beaconBlock(ctx, 1, []byte{1}, []byte{2}, nil, nil) assert.ErrorContains(t, testCase.expectedErrorMessage, err) }) } @@ -199,7 +199,7 @@ func TestGetBeaconBlock_Phase0Valid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -261,7 +261,7 @@ func TestGetBeaconBlock_SSZ_BellatrixValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -303,7 +303,7 @@ func TestGetBeaconBlock_SSZ_BlindedBellatrixValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -345,7 +345,7 @@ func TestGetBeaconBlock_SSZ_CapellaValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -387,7 +387,7 @@ func TestGetBeaconBlock_SSZ_BlindedCapellaValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -429,7 +429,7 @@ func TestGetBeaconBlock_SSZ_DenebValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -471,7 +471,7 @@ func TestGetBeaconBlock_SSZ_BlindedDenebValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -513,7 +513,7 @@ func TestGetBeaconBlock_SSZ_ElectraValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -555,7 +555,7 @@ func TestGetBeaconBlock_SSZ_BlindedElectraValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -597,7 +597,7 @@ func TestGetBeaconBlock_SSZ_FuluValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -639,7 +639,7 @@ func TestGetBeaconBlock_SSZ_BlindedFuluValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -677,7 +677,7 @@ func TestGetBeaconBlock_SSZ_UnsupportedVersion(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - _, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + _, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) assert.ErrorContains(t, "version name doesn't map to a known value in the enum", err) } @@ -710,7 +710,7 @@ func TestGetBeaconBlock_SSZ_InvalidBlindedHeader(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - _, err = validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + _, err = validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) assert.ErrorContains(t, "strconv.ParseBool: parsing \"invalid\": invalid syntax", err) } @@ -743,7 +743,7 @@ func TestGetBeaconBlock_SSZ_InvalidVersionHeader(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - _, err = validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + _, err = validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) assert.ErrorContains(t, "unsupported header version invalid", err) } @@ -768,7 +768,7 @@ func TestGetBeaconBlock_SSZ_GetSSZError(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - _, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + _, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) assert.ErrorContains(t, "get ssz error", err) } @@ -801,7 +801,7 @@ func TestGetBeaconBlock_SSZ_Phase0Valid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -843,7 +843,7 @@ func TestGetBeaconBlock_SSZ_AltairValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -887,7 +887,7 @@ func TestGetBeaconBlock_AltairValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -931,7 +931,7 @@ func TestGetBeaconBlock_BellatrixValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -976,7 +976,7 @@ func TestGetBeaconBlock_BlindedBellatrixValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1021,7 +1021,7 @@ func TestGetBeaconBlock_CapellaValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1066,7 +1066,7 @@ func TestGetBeaconBlock_BlindedCapellaValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1111,7 +1111,7 @@ func TestGetBeaconBlock_FuluValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1156,7 +1156,7 @@ func TestGetBeaconBlock_BlindedFuluValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1201,7 +1201,7 @@ func TestGetBeaconBlock_DenebValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1246,7 +1246,7 @@ func TestGetBeaconBlock_BlindedDenebValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1291,7 +1291,7 @@ func TestGetBeaconBlock_ElectraValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1336,7 +1336,7 @@ func TestGetBeaconBlock_BlindedElectraValid(t *testing.T) { ).Times(1) validatorClient := &beaconApiValidatorClient{handler: handler} - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1395,7 +1395,7 @@ func TestGetBeaconBlock_GloasValid_SSZ_WithPayload(t *testing.T) { stateless: true, envelopeCache: cache.NewExecutionPayloadEnvelopeCache(), } - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1443,7 +1443,7 @@ func TestGetBeaconBlock_GloasValid_SSZ_WithoutPayload(t *testing.T) { handler: handler, envelopeCache: cache.NewExecutionPayloadEnvelopeCache(), } - beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(ctx, slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) expectedBeaconBlock := ðpb.GenericBeaconBlock{ @@ -1490,7 +1490,7 @@ func TestGetBeaconBlock_GloasValid_JSON_WithoutPayload(t *testing.T) { handler: handler, envelopeCache: cache.NewExecutionPayloadEnvelopeCache(), } - beaconBlock, err := validatorClient.beaconBlock(t.Context(), slot, randaoReveal, graffiti) + beaconBlock, err := validatorClient.beaconBlock(t.Context(), slot, randaoReveal, graffiti, nil, nil) require.NoError(t, err) assert.DeepEqual(t, ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Gloas{Gloas: proto}}, beaconBlock) @@ -1523,6 +1523,6 @@ func TestGetBeaconBlock_GloasRejectsJSONWithPayload(t *testing.T) { stateless: true, envelopeCache: cache.NewExecutionPayloadEnvelopeCache(), } - _, err := validatorClient.beaconBlock(t.Context(), slot, randaoReveal, graffiti) + _, err := validatorClient.beaconBlock(t.Context(), slot, randaoReveal, graffiti, nil, nil) assert.ErrorContains(t, "must be SSZ", err) } diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index 928e61e545a0..921a3dab2572 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -304,9 +304,6 @@ func (c *grpcValidatorClient) SubmitSignedProposerPreferences(ctx context.Contex return c.getClient().SubmitSignedProposerPreferences(ctx, in) } -func (c *grpcValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { - return c.getClient().SubmitBuilderPreferences(ctx, in) -} func (c *grpcValidatorClient) SubmitSignedExecutionPayloadBid(ctx context.Context, in *ethpb.SignedExecutionPayloadBid) (*empty.Empty, error) { return c.getClient().SubmitSignedExecutionPayloadBid(ctx, in) diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index bd4775fbc009..09d30755111a 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -158,7 +158,6 @@ type ValidatorClient interface { // proposal slots. This replaces PrepareBeaconProposer and SubmitValidatorRegistrations // for Gloas+. SubmitSignedProposerPreferences(ctx context.Context, in *ethpb.SubmitSignedProposerPreferencesRequest) (*empty.Empty, error) - SubmitBuilderPreferences(ctx context.Context, in *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) SubmitSignedExecutionPayloadBid(ctx context.Context, in *ethpb.SignedExecutionPayloadBid) (*empty.Empty, error) StartEventStream(ctx context.Context, topics []string, eventsChannel chan<- *event.Event) EventStreamIsRunning() bool diff --git a/validator/client/propose.go b/validator/client/propose.go index 9681cc7f41f2..0e06dc058693 100644 --- a/validator/client/propose.go +++ b/validator/client/propose.go @@ -29,6 +29,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/wrapperspb" ) const ( @@ -79,12 +80,16 @@ func (v *validator) ProposeBlock(ctx context.Context, slot primitives.Slot, pubK } // Request block from beacon node - b, err := v.validatorClient.BeaconBlock(ctx, ðpb.BlockRequest{ - Slot: slot, - RandaoReveal: randaoReveal, - Graffiti: g, - BuilderRequestAuths: v.builderRequestAuthsForSlot(pubKey, slot), - }) + blockRequest := ðpb.BlockRequest{ + Slot: slot, + RandaoReveal: randaoReveal, + Graffiti: g, + BuilderPreferences: v.buildBuilderPreferencesForSlot(pubKey, slot), + } + if bc := v.builderConfigForKey(pubKey); bc.IsEnabled() && bc.BuilderBoostFactor != nil { + blockRequest.BuilderBoostFactor = wrapperspb.UInt64(uint64(*bc.BuilderBoostFactor)) + } + b, err := v.validatorClient.BeaconBlock(ctx, blockRequest) if err != nil { log.WithField("slot", slot).WithError(err).Error("Failed to request block from beacon node") if v.emitAccountMetrics { diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 7eab7a39af14..096145b1d4c6 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -11,6 +11,13 @@ import ( "time" "github.com/OffchainLabs/go-bitfield" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + logTest "github.com/sirupsen/logrus/hooks/test" + "go.uber.org/mock/gomock" + "google.golang.org/protobuf/proto" + "github.com/OffchainLabs/prysm/v7/async/event" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" @@ -30,11 +37,6 @@ import ( testing2 "github.com/OffchainLabs/prysm/v7/validator/db/testing" "github.com/OffchainLabs/prysm/v7/validator/keymanager" "github.com/OffchainLabs/prysm/v7/validator/keymanager/local" - "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - logTest "github.com/sirupsen/logrus/hooks/test" - "go.uber.org/mock/gomock" ) func cancelledContext() context.Context { @@ -568,7 +570,7 @@ func TestRunnerPushesProposerSettings_ValidContext(t *testing.T) { FeeRecipient: common.BytesToAddress([]byte{1}), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 60_000_000, Relays: []string{"https://example.com"}, }, diff --git a/validator/client/validator.go b/validator/client/validator.go index 07e0c4f960f2..bd08df2ec688 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -27,6 +27,7 @@ import ( "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + validatortypes "github.com/OffchainLabs/prysm/v7/consensus-types/validator" "github.com/OffchainLabs/prysm/v7/crypto/hash" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" @@ -885,15 +886,7 @@ func (v *validator) PushProposerSettings(ctx context.Context, slot primitives.Sl v.connTracker.confirm(proposerPrefsPush, connGen) } - if reqs := v.buildBuilderPreferenceRequests(ctx, km, slot); len(reqs) > 0 { - delay := params.BeaconConfig().SlotDuration() / 2 - time.AfterFunc(delay, func() { - // Detached from the slot context, which may expire before the delay elapses. - subCtx, cancel := context.WithTimeout(context.Background(), delay) - defer cancel() - v.submitBuilderPreferenceRequests(subCtx, reqs) - }) - } + v.warmBuilderRequestAuths(ctx, km, slot) // TODO: figure out what to do post gloas for builder apis if !isPreGloas { @@ -1356,10 +1349,19 @@ func (v *validator) submittedPrefSlotsCount() int { return len(v.submittedPrefSlots) } -func (v *validator) builderConfigForKey(pk pubkey) ([]string, uint64, bool) { +// builderTarget is one resolved builder for a proposer: a URL plus the +// preferences to submit for it, with per-entry overrides already applied. +type builderTarget struct { + url string + maxPayment uint64 + minBid *uint64 + boostFactor *uint64 +} + +func (v *validator) builderConfigForKey(pk pubkey) *proposer.BuilderConfig { ps := v.ProposerSettings() if ps == nil { - return nil, 0, false + return nil } var bc *proposer.BuilderConfig if ps.DefaultConfig != nil { @@ -1370,63 +1372,119 @@ func (v *validator) builderConfigForKey(pk pubkey) ([]string, uint64, bool) { bc = c.BuilderConfig } } - if bc == nil { - return nil, 0, false + return bc +} + +// builderTargetsForKey resolves the enabled builder list for pk. Each Builders[] +// entry overrides the config-level fallbacks; legacy Relays are url-only targets. +func (v *validator) builderTargetsForKey(pk pubkey) []builderTarget { + bc := v.builderConfigForKey(pk) + if !bc.IsEnabled() { + return nil + } + fbMax := uint64(bc.MaxExecutionPayment) + fbMin := uint64Ptr(bc.MinBid) + fbBoost := uint64Ptr(bc.BuilderBoostFactor) + + targets := make([]builderTarget, 0, len(bc.Builders)+len(bc.Relays)) + for _, e := range bc.Builders { + if e == nil || e.URL == "" { + continue + } + t := builderTarget{url: e.URL, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost} + if e.MaxExecutionPayment != nil { + t.maxPayment = uint64(*e.MaxExecutionPayment) + } + if e.MinBid != nil { + t.minBid = uint64Ptr(e.MinBid) + } + if e.BuilderBoostFactor != nil { + t.boostFactor = uint64Ptr(e.BuilderBoostFactor) + } + targets = append(targets, t) + } + for _, relay := range bc.Relays { + targets = append(targets, builderTarget{url: relay, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost}) } - return bc.Relays, uint64(bc.MaxExecutionPayment), bc.Enabled + return targets } -// Resubmitted every push to repopulate a restarted beacon node, using cached auths to avoid re-signing. -func (v *validator) buildBuilderPreferenceRequests(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) []*ethpb.SubmitBuilderPreferencesRequest { - if slots.ToEpoch(slot)+1 < params.BeaconConfig().GloasForkEpoch { +func uint64Ptr(v *validatortypes.Uint64) *uint64 { + if v == nil { return nil } + u := uint64(*v) + return &u +} + +// warmBuilderRequestAuths pre-signs the builder request auths for upcoming +// proposal slots so proposeBlock can attach them inline (JIT) without signing on +// the hot path. It no longer submits to the beacon node: preferences now travel +// with the block request. +func (v *validator) warmBuilderRequestAuths(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) { + if slots.ToEpoch(slot)+1 < params.BeaconConfig().GloasForkEpoch { + return + } snap := v.duties.snapshot() if !snap.isInitialized() { - return nil + return } v.pruneSignedRequestAuths(slot) - - reqs := v.builderPreferenceRequestsForDuties(ctx, km, slot, snap.currentDuties()) - return append(reqs, v.builderPreferenceRequestsForDuties(ctx, km, slot, snap.nextDuties())...) + v.warmBuilderRequestAuthsForDuties(ctx, km, slot, snap.currentDuties()) + v.warmBuilderRequestAuthsForDuties(ctx, km, slot, snap.nextDuties()) } -func (v *validator) builderPreferenceRequestsForDuties(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, duties iter.Seq2[pubkey, *ethpb.ValidatorDuty]) []*ethpb.SubmitBuilderPreferencesRequest { - var reqs []*ethpb.SubmitBuilderPreferencesRequest +func (v *validator) warmBuilderRequestAuthsForDuties(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, duties iter.Seq2[pubkey, *ethpb.ValidatorDuty]) { for pk, duty := range duties { - relays, maxPayment, enabled := v.builderConfigForKey(pk) - if !enabled || len(relays) == 0 { + targets := v.builderTargetsForKey(pk) + if len(targets) == 0 { continue } for _, proposalSlot := range duty.ProposerSlots { if proposalSlot <= slot { continue } - for _, relay := range relays { - signed, err := v.signRequestAuthCached(ctx, km, pk, relay, proposalSlot) - if err != nil { + for _, t := range targets { + if _, err := v.signRequestAuthCached(ctx, km, pk, t.url, proposalSlot); err != nil { log.WithError(err).Warn("Failed to sign builder request auth") - continue } - reqs = append(reqs, ðpb.SubmitBuilderPreferencesRequest{ - ValidatorPubkey: pk[:], - Request: ðpb.BuilderPreferencesRequestV1{ - Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(maxPayment)}, - Auth: signed, - }, - }) } } } - return reqs } -func (v *validator) submitBuilderPreferenceRequests(ctx context.Context, reqs []*ethpb.SubmitBuilderPreferencesRequest) { - for _, req := range reqs { - if _, err := v.validatorClient.SubmitBuilderPreferences(ctx, req); err != nil { - log.WithError(err).Warn("Failed to submit builder preferences") +// buildBuilderPreferencesForSlot assembles the per-builder preferences to carry +// inline on the block request for pk's proposal at slot, pairing each cached +// signed auth with its resolved caps. +func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Slot) []*ethpb.BuilderPreferenceV1 { + auths := v.builderRequestAuthsForSlot(pk, slot) + if len(auths) == 0 { + return nil + } + capsByURL := make(map[string]builderTarget) + for _, t := range v.builderTargetsForKey(pk) { + capsByURL[t.url] = t + } + prefs := make([]*ethpb.BuilderPreferenceV1, 0, len(auths)) + for _, auth := range auths { + t, ok := capsByURL[string(auth.GetMessage().GetData())] + if !ok { + continue + } + p := ðpb.BuilderPreferenceV1{ + Request: ðpb.BuilderPreferencesRequestV1{ + Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(t.maxPayment)}, + Auth: auth, + }, + BuilderBoostFactor: t.boostFactor, + } + if t.minBid != nil { + g := primitives.Gwei(*t.minBid) + p.MinBid = &g } + prefs = append(prefs, p) } + return prefs } // submitProposerPreferences builds and submits proposer preferences for the @@ -1503,7 +1561,7 @@ func (v *validator) buildSignedRegReqs( feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient // Use cli defaultBuilderConfig for fee recipient. defaultBuilderConfig := defaultConfig.BuilderConfig - if defaultBuilderConfig != nil && defaultBuilderConfig.Enabled { + if defaultBuilderConfig.IsEnabled() { gasLimit = uint64(defaultBuilderConfig.GasLimit) // Use cli config for gas limit. enabled = true } @@ -1515,7 +1573,7 @@ func (v *validator) buildSignedRegReqs( feeRecipient = config.FeeRecipientConfig.FeeRecipient // Use file config for fee recipient. builderConfig := config.BuilderConfig if builderConfig != nil { - if builderConfig.Enabled { + if builderConfig.IsEnabled() { gasLimit = uint64(builderConfig.GasLimit) // Use file config for gas limit. enabled = true } else { diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index fb3fe9d643c1..f50d5ec87627 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -1104,7 +1104,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 40000000, }, } @@ -1115,7 +1115,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress(defaultFeeHex), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 35000000, }, }, @@ -1190,7 +1190,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress("0x055Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 40000000, }, } @@ -1201,7 +1201,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress(defaultFeeHex), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: 35000000, }, }, @@ -1316,7 +1316,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress(defaultFeeHex), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validatorType.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -1377,7 +1377,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress(defaultFeeHex), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 40000000, }, }, @@ -1552,7 +1552,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.Address{}, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 40000000, }, } @@ -1563,7 +1563,7 @@ func TestValidator_PushSettings(t *testing.T) { FeeRecipient: common.HexToAddress(defaultFeeHex), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 40000000, }, }, @@ -2411,7 +2411,7 @@ func TestValidator_buildProposerPreferences(t *testing.T) { FeeRecipient: feeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 42000000, }, }, @@ -2976,7 +2976,7 @@ func TestValidator_buildProposerPreferences_GasLimitSources(t *testing.T) { settings: &proposer.Settings{ DefaultConfig: &proposer.Option{ FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: feeRecipient}, - BuilderConfig: &proposer.BuilderConfig{Enabled: true, GasLimit: 42000000}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 42000000}, }, }, needsDB: true, @@ -2992,7 +2992,7 @@ func TestValidator_buildProposerPreferences_GasLimitSources(t *testing.T) { settings: &proposer.Settings{ DefaultConfig: &proposer.Option{ FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: feeRecipient}, - BuilderConfig: &proposer.BuilderConfig{Enabled: true, GasLimit: 42000000}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 42000000}, }, }, needsDB: true, @@ -3139,7 +3139,7 @@ func TestValidator_PushProposerSettings_SkipsBuilderRegistrationsPostGloas(t *te FeeRecipientConfig: &proposer.FeeRecipientConfig{ FeeRecipient: common.HexToAddress("0x046Fb65722E7b2455043BFEBf6177F1D2e9738D9"), }, - BuilderConfig: &proposer.BuilderConfig{Enabled: true, GasLimit: 40000000}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 40000000}, }, })) @@ -3275,7 +3275,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { FeeRecipient: defaultFeeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: 9999, }, }, @@ -3285,7 +3285,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { FeeRecipient: feeRecipient1, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 1111, }, }, @@ -3294,14 +3294,14 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { FeeRecipient: feeRecipient2, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: 2222, }, }, pubkey3: { FeeRecipientConfig: nil, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 3333, }, }, @@ -3374,7 +3374,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { FeeRecipient: defaultFeeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 9999, }, }, @@ -3384,7 +3384,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { FeeRecipient: feeRecipient1, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 1111, }, }, @@ -3393,14 +3393,14 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { FeeRecipient: feeRecipient2, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: 2222, }, }, pubkey3: { FeeRecipientConfig: nil, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 3333, }, }, @@ -3492,7 +3492,7 @@ func TestValidator_buildSignedRegReqs_V2Settings(t *testing.T) { FeeRecipient: defaultFeeRecipient, }, GasLimit: 8888, - BuilderConfig: &proposer.BuilderConfig{Enabled: true, GasLimit: 9999}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 9999}, }, ProposeConfig: map[[48]byte]*proposer.Option{ pubkey1: { @@ -3500,7 +3500,7 @@ func TestValidator_buildSignedRegReqs_V2Settings(t *testing.T) { FeeRecipient: feeRecipient1, }, GasLimit: 1111, - BuilderConfig: &proposer.BuilderConfig{Enabled: true, GasLimit: 7777}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 7777}, }, pubkey2: { FeeRecipientConfig: &proposer.FeeRecipientConfig{ @@ -3512,7 +3512,7 @@ func TestValidator_buildSignedRegReqs_V2Settings(t *testing.T) { FeeRecipientConfig: &proposer.FeeRecipientConfig{ FeeRecipient: feeRecipient2, }, - BuilderConfig: &proposer.BuilderConfig{Enabled: false}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(false)}, }, }, }, @@ -3566,7 +3566,7 @@ func TestValidator_buildSignedRegReqs_SignerOnError(t *testing.T) { FeeRecipient: defaultFeeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 9999, }, }, @@ -3610,7 +3610,7 @@ func TestValidator_buildSignedRegReqs_TimestampBeforeGenesis(t *testing.T) { FeeRecipient: defaultFeeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 9999, }, }, @@ -3620,7 +3620,7 @@ func TestValidator_buildSignedRegReqs_TimestampBeforeGenesis(t *testing.T) { FeeRecipient: feeRecipient1, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 1111, }, }, diff --git a/validator/db/BUILD.bazel b/validator/db/BUILD.bazel index ad4ae74a13f4..131cb9964289 100644 --- a/validator/db/BUILD.bazel +++ b/validator/db/BUILD.bazel @@ -57,5 +57,6 @@ go_test( "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@com_github_urfave_cli_v2//:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/validator/db/convert_test.go b/validator/db/convert_test.go index 059a7172654d..6c53fbb5700d 100644 --- a/validator/db/convert_test.go +++ b/validator/db/convert_test.go @@ -5,6 +5,9 @@ import ( "path/filepath" "testing" + "github.com/ethereum/go-ethereum/common/hexutil" + "google.golang.org/protobuf/proto" + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" @@ -15,7 +18,6 @@ import ( "github.com/OffchainLabs/prysm/v7/validator/db/filesystem" "github.com/OffchainLabs/prysm/v7/validator/db/iface" "github.com/OffchainLabs/prysm/v7/validator/db/kv" - "github.com/ethereum/go-ethereum/common/hexutil" ) func getPubkeyFromString(t *testing.T, pubkeyString string) [fieldparams.BLSPubkeyLength]byte { @@ -108,7 +110,7 @@ func TestDB_ConvertDatabase(t *testing.T) { FeeRecipient: customFeeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 42, Relays: relays, }, @@ -119,7 +121,7 @@ func TestDB_ConvertDatabase(t *testing.T) { FeeRecipient: defaultFeeRecipient, }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: 43, Relays: relays, }, diff --git a/validator/db/kv/BUILD.bazel b/validator/db/kv/BUILD.bazel index 7c085773e8f1..89f09b7ac5be 100644 --- a/validator/db/kv/BUILD.bazel +++ b/validator/db/kv/BUILD.bazel @@ -95,5 +95,6 @@ go_test( "@com_github_sirupsen_logrus//:go_default_library", "@com_github_sirupsen_logrus//hooks/test:go_default_library", "@io_etcd_go_bbolt//:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/validator/db/kv/proposer_settings_test.go b/validator/db/kv/proposer_settings_test.go index b32a3d87267c..2ad05f1f1906 100644 --- a/validator/db/kv/proposer_settings_test.go +++ b/validator/db/kv/proposer_settings_test.go @@ -3,14 +3,16 @@ package kv import ( "testing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "google.golang.org/protobuf/proto" + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/validator" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" "github.com/OffchainLabs/prysm/v7/testing/require" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" ) func TestStore_ProposerSettings_ReadAndWrite(t *testing.T) { @@ -26,7 +28,7 @@ func TestStore_ProposerSettings_ReadAndWrite(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, }, @@ -36,7 +38,7 @@ func TestStore_ProposerSettings_ReadAndWrite(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -59,7 +61,7 @@ func TestStore_ProposerSettings_ReadAndWrite(t *testing.T) { FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: false, + Enabled: proto.Bool(false), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, }, @@ -71,7 +73,7 @@ func TestStore_ProposerSettings_ReadAndWrite(t *testing.T) { FeeRecipient: common.HexToAddress("0x9995733c5af9B61374A128e6F85f553aF09ff89B"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), }, } @@ -88,7 +90,7 @@ func TestStore_ProposerSettings_ReadAndWrite(t *testing.T) { FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), }, BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), }, } diff --git a/validator/rpc/BUILD.bazel b/validator/rpc/BUILD.bazel index 5b08ca61782c..a0dc809979e8 100644 --- a/validator/rpc/BUILD.bazel +++ b/validator/rpc/BUILD.bazel @@ -12,6 +12,7 @@ go_library( "handlers_health.go", "handlers_keymanager.go", "handlers_slashing.go", + "handlers_validator_config.go", "intercepter.go", "log.go", "server.go", @@ -94,6 +95,7 @@ go_test( "handlers_health_test.go", "handlers_keymanager_test.go", "handlers_slashing_test.go", + "handlers_validator_config_test.go", "intercepter_test.go", "server_test.go", ], @@ -148,6 +150,7 @@ go_test( "@org_golang_google_grpc//codes:go_default_library", "@org_golang_google_grpc//metadata:go_default_library", "@org_golang_google_grpc//status:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", "@org_golang_google_protobuf//types/known/emptypb:go_default_library", "@org_golang_google_protobuf//types/known/timestamppb:go_default_library", "@org_uber_go_mock//gomock:go_default_library", diff --git a/validator/rpc/handlers_keymanager.go b/validator/rpc/handlers_keymanager.go index 87adc93c9ef0..d32d7208a965 100644 --- a/validator/rpc/handlers_keymanager.go +++ b/validator/rpc/handlers_keymanager.go @@ -624,6 +624,8 @@ func (s *Server) SetFeeRecipientByPubkey(w http.ResponseWriter, r *http.Request) return } feeRecipient := common.BytesToAddress(ethAddress) + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() settings := s.validatorService.ProposerSettings() switch { case settings == nil: @@ -693,6 +695,8 @@ func (s *Server) DeleteFeeRecipientByPubkey(w http.ResponseWriter, r *http.Reque return } + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() settings := s.validatorService.ProposerSettings() if settings != nil && settings.ProposeConfig != nil { proposerOption, found := settings.ProposeConfig[bytesutil.ToBytes48(pubkey)] @@ -764,6 +768,8 @@ func (s *Server) SetGasLimit(w http.ResponseWriter, r *http.Request) { return } + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() settings := s.validatorService.ProposerSettings() if err := settings.SetGasLimit(bytesutil.ToBytes48(pubkey), validator.Uint64(gasLimit)); err != nil { httputil.HandleError(w, err.Error(), http.StatusInternalServerError) @@ -791,6 +797,8 @@ func (s *Server) DeleteGasLimit(w http.ResponseWriter, r *http.Request) { return } + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() settings := s.validatorService.ProposerSettings() if !settings.ResetGasLimit(bytesutil.ToBytes48(pubkey)) { httputil.HandleError(w, fmt.Sprintf("No gas limit found for pubkey %q", rawPubkey), http.StatusNotFound) diff --git a/validator/rpc/handlers_keymanager_test.go b/validator/rpc/handlers_keymanager_test.go index d01e742bf271..e6c77ae51cf8 100644 --- a/validator/rpc/handlers_keymanager_test.go +++ b/validator/rpc/handlers_keymanager_test.go @@ -13,6 +13,14 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/urfave/cli/v2" + "go.uber.org/mock/gomock" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/emptypb" + "google.golang.org/protobuf/types/known/timestamppb" + "github.com/OffchainLabs/prysm/v7/cmd/validator/flags" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/params" @@ -39,12 +47,6 @@ import ( remoteweb3signer "github.com/OffchainLabs/prysm/v7/validator/keymanager/remote-web3signer" "github.com/OffchainLabs/prysm/v7/validator/slashing-protection-history/format" mocks "github.com/OffchainLabs/prysm/v7/validator/testing" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/urfave/cli/v2" - "go.uber.org/mock/gomock" - "google.golang.org/protobuf/types/known/emptypb" - "google.golang.org/protobuf/types/known/timestamppb" ) func TestServer_ListKeystores(t *testing.T) { @@ -1075,7 +1077,7 @@ func TestServer_SetGasLimit(t *testing.T) { ProposeConfig: map[[48]byte]*proposer.Option{ bytesutil.ToBytes48(pubkey2): { BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), GasLimit: 12345, }, }, @@ -1100,7 +1102,7 @@ func TestServer_SetGasLimit(t *testing.T) { }, DefaultConfig: &proposer.Option{ BuilderConfig: &proposer.BuilderConfig{ - Enabled: true, + Enabled: proto.Bool(true), }, }, }, diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go new file mode 100644 index 000000000000..75c0bbbd5dbb --- /dev/null +++ b/validator/rpc/handlers_validator_config.go @@ -0,0 +1,374 @@ +package rpc + +import ( + "encoding/json" + "net/http" + "strconv" + + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + "github.com/OffchainLabs/prysm/v7/config/proposer" + "github.com/OffchainLabs/prysm/v7/consensus-types/validator" + "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" + "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" + "github.com/OffchainLabs/prysm/v7/network/httputil" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" +) + +const ( + configStatusSet = "set" + configStatusNotFound = "not_found" + configStatusError = "error" +) + +// GetValidatorConfig implements GET /eth/v1/validator/config from keymanager-APIs #87. +// It returns the explicitly configured per-key fragments plus the read-only default_config. +func (s *Server) GetValidatorConfig(w http.ResponseWriter, r *http.Request) { + _, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.GetValidatorConfig") + defer span.End() + + if s.validatorService == nil { + httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable) + return + } + + filter, ok := parsePubkeyFilter(w, r) + if !ok { + return + } + + settings := s.validatorService.ProposerSettings() + resp := &GetValidatorConfigResponse{Data: &ValidatorConfigData{Configs: map[string]*ValidatorConfig{}}} + if settings == nil { + httputil.WriteJson(w, resp) + return + } + if settings.DefaultConfig != nil { + resp.Data.DefaultConfig = validatorConfigFromOption(settings.DefaultConfig) + } + for pk, opt := range settings.ProposeConfig { + if filter != nil { + if _, wanted := filter[pk]; !wanted { + continue + } + } + resp.Data.Configs[hexutil.Encode(pk[:])] = validatorConfigFromOption(opt) + } + httputil.WriteJson(w, resp) +} + +// SetValidatorConfig implements POST /eth/v1/validator/config from keymanager-APIs #87. +// Each submitted document REPLACES the key's configured fragment. An empty document +// clears the key. Keys are applied independently with a per-key status. +func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.SetValidatorConfig") + defer span.End() + + if s.validatorService == nil { + httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable) + return + } + + var req SetValidatorConfigRequest + dec := json.NewDecoder(r.Body) + dec.DisallowUnknownFields() + if err := dec.Decode(&req); err != nil { + httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + return + } + if req.Configs == nil { + httputil.HandleError(w, "No configs submitted", http.StatusBadRequest) + return + } + + km, err := s.validatorService.Keymanager() + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusInternalServerError) + return + } + known, err := km.FetchValidatingPublicKeys(ctx) + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusInternalServerError) + return + } + knownSet := make(map[[fieldparams.BLSPubkeyLength]byte]bool, len(known)) + for _, pk := range known { + knownSet[pk] = true + } + + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() + + settings := s.validatorService.ProposerSettings() + if settings == nil { + settings = &proposer.Settings{Version: proposer.SchemaV2} + } else { + settings = settings.Clone() + settings.UpgradeToV2() + } + if settings.ProposeConfig == nil { + settings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option) + } + + resp := &SetValidatorConfigResponse{Data: make(map[string]*ValidatorConfigStatus, len(req.Configs))} + changed := false + for rawPubkey, cfg := range req.Configs { + pubkey, err := decodePubkey(rawPubkey) + if err != nil { + resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusError, Message: err.Error()} + continue + } + if !knownSet[pubkey] { + resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusNotFound} + continue + } + if cfg == nil || cfg.isEmpty() { + delete(settings.ProposeConfig, pubkey) + resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusSet} + changed = true + continue + } + opt, err := optionFromValidatorConfig(cfg) + if err != nil { + resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusError, Message: err.Error()} + continue + } + settings.ProposeConfig[pubkey] = opt + resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusSet} + changed = true + } + + if changed { + if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil { + httputil.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError) + return + } + } + httputil.WriteJson(w, resp) +} + +func parsePubkeyFilter(w http.ResponseWriter, r *http.Request) (map[[fieldparams.BLSPubkeyLength]byte]bool, bool) { + raw := r.URL.Query()["pubkeys"] + if len(raw) == 0 { + return nil, true + } + filter := make(map[[fieldparams.BLSPubkeyLength]byte]bool, len(raw)) + for _, p := range raw { + pubkey, err := decodePubkey(p) + if err != nil { + httputil.HandleError(w, "Invalid pubkey "+p+": "+err.Error(), http.StatusBadRequest) + return nil, false + } + filter[pubkey] = true + } + return filter, true +} + +func decodePubkey(raw string) ([fieldparams.BLSPubkeyLength]byte, error) { + var out [fieldparams.BLSPubkeyLength]byte + decoded, err := hexutil.Decode(raw) + if err != nil { + return out, errors.Wrap(err, "invalid public key") + } + if len(decoded) != fieldparams.BLSPubkeyLength { + return out, errors.Errorf("public key %s is not %d bytes", raw, fieldparams.BLSPubkeyLength) + } + return bytesutil.ToBytes48(decoded), nil +} + +func (c *ValidatorConfig) isEmpty() bool { + return c.FeeRecipient == nil && c.TargetGasLimit == nil && c.Graffiti == nil && c.Builder == nil +} + +// validatorConfigFromOption serializes only the explicitly configured fields, so +// tooling can tell an explicit value from an inherited one. +func validatorConfigFromOption(opt *proposer.Option) *ValidatorConfig { + cfg := &ValidatorConfig{} + if opt == nil { + return cfg + } + if opt.FeeRecipientConfig != nil { + addr := opt.FeeRecipientConfig.FeeRecipient.Hex() + cfg.FeeRecipient = &addr + } + if opt.GasLimit != 0 { + cfg.TargetGasLimit = strPtr(formatUint(opt.GasLimit)) + } + if opt.GraffitiConfig != nil { + g := opt.GraffitiConfig.Graffiti + cfg.Graffiti = &g + } + if opt.BuilderConfig != nil { + cfg.Builder = builderConfigJSONFromConsensus(opt.BuilderConfig) + } + return cfg +} + +func builderConfigJSONFromConsensus(bc *proposer.BuilderConfig) *BuilderConfigJson { + out := &BuilderConfigJson{ + Enabled: bc.Enabled, + Proxy: bc.Proxy, + MaxExecutionPayment: uintStrPtr(bc.MaxExecutionPayment), + MinBid: optUintStrPtr(bc.MinBid), + BuilderBoostFactor: optUintStrPtr(bc.BuilderBoostFactor), + } + for _, b := range bc.Builders { + out.Builders = append(out.Builders, builderEntryJSONFromConsensus(b)) + } + return out +} + +func builderEntryJSONFromConsensus(be *proposer.BuilderEntry) *BuilderEntryJson { + out := &BuilderEntryJson{ + Url: be.URL, + Proxy: be.Proxy, + MaxExecutionPayment: optUintStrPtr(be.MaxExecutionPayment), + MinBid: optUintStrPtr(be.MinBid), + BuilderBoostFactor: optUintStrPtr(be.BuilderBoostFactor), + } + if len(be.Pubkey) != 0 { + out.Pubkey = strPtr(hexutil.Encode(be.Pubkey)) + } + if len(be.AuthData) != 0 { + out.AuthData = strPtr(hexutil.Encode(be.AuthData)) + } + return out +} + +// optionFromValidatorConfig builds a fresh Option from a submitted document. Every +// field is validated; unset fields stay nil so they inherit from default_config. +func optionFromValidatorConfig(cfg *ValidatorConfig) (*proposer.Option, error) { + opt := &proposer.Option{} + if cfg.FeeRecipient != nil { + if !common.IsHexAddress(*cfg.FeeRecipient) { + return nil, errors.Errorf("fee_recipient %s is not a valid execution address", *cfg.FeeRecipient) + } + opt.FeeRecipientConfig = &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress(*cfg.FeeRecipient)} + } + if cfg.TargetGasLimit != nil { + v, err := parseUint(*cfg.TargetGasLimit, "target_gas_limit") + if err != nil { + return nil, err + } + opt.GasLimit = v + } + if cfg.Graffiti != nil { + opt.GraffitiConfig = &proposer.GraffitiConfig{Graffiti: *cfg.Graffiti} + } + if cfg.Builder != nil { + bc, err := builderConfigFromJSON(cfg.Builder) + if err != nil { + return nil, err + } + opt.BuilderConfig = bc + } + return opt, nil +} + +func builderConfigFromJSON(in *BuilderConfigJson) (*proposer.BuilderConfig, error) { + bc := &proposer.BuilderConfig{Enabled: in.Enabled, Proxy: in.Proxy} + if in.MaxExecutionPayment != nil { + v, err := parseUint(*in.MaxExecutionPayment, "builder.max_execution_payment") + if err != nil { + return nil, err + } + bc.MaxExecutionPayment = v + } + if in.MinBid != nil { + v, err := parseUint(*in.MinBid, "builder.min_bid") + if err != nil { + return nil, err + } + bc.MinBid = &v + } + if in.BuilderBoostFactor != nil { + v, err := parseUint(*in.BuilderBoostFactor, "builder.builder_boost_factor") + if err != nil { + return nil, err + } + bc.BuilderBoostFactor = &v + } + for i, entry := range in.Builders { + be, err := builderEntryFromJSON(entry, i) + if err != nil { + return nil, err + } + bc.Builders = append(bc.Builders, be) + } + return bc, nil +} + +func builderEntryFromJSON(in *BuilderEntryJson, i int) (*proposer.BuilderEntry, error) { + if in.Url == "" { + return nil, errors.Errorf("builder.builders[%d].url is required", i) + } + be := &proposer.BuilderEntry{URL: in.Url, Proxy: in.Proxy} + if in.Pubkey != nil { + pk, err := hexutil.Decode(*in.Pubkey) + if err != nil || len(pk) != fieldparams.BLSPubkeyLength { + return nil, errors.Errorf("builder.builders[%d].pubkey is not a valid BLS public key", i) + } + be.Pubkey = pk + } + if in.AuthData != nil { + ad, err := hexutil.Decode(*in.AuthData) + if err != nil { + return nil, errors.Errorf("builder.builders[%d].auth_data is not valid hex", i) + } + if len(ad) > 4096 { + return nil, errors.Errorf("builder.builders[%d].auth_data exceeds 4096 bytes", i) + } + be.AuthData = ad + } + if in.MaxExecutionPayment != nil { + v, err := parseUint(*in.MaxExecutionPayment, "builder.builders[].max_execution_payment") + if err != nil { + return nil, err + } + be.MaxExecutionPayment = &v + } + if in.MinBid != nil { + v, err := parseUint(*in.MinBid, "builder.builders[].min_bid") + if err != nil { + return nil, err + } + be.MinBid = &v + } + if in.BuilderBoostFactor != nil { + v, err := parseUint(*in.BuilderBoostFactor, "builder.builders[].builder_boost_factor") + if err != nil { + return nil, err + } + be.BuilderBoostFactor = &v + } + return be, nil +} + +func parseUint(s, field string) (validator.Uint64, error) { + v, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return 0, errors.Errorf("%s is not a valid uint64", field) + } + return validator.Uint64(v), nil +} + +func formatUint(v validator.Uint64) string { + return strconv.FormatUint(uint64(v), 10) +} + +func strPtr(s string) *string { return &s } + +func uintStrPtr(v validator.Uint64) *string { + if v == 0 { + return nil + } + return strPtr(formatUint(v)) +} + +func optUintStrPtr(v *validator.Uint64) *string { + if v == nil { + return nil + } + return strPtr(formatUint(*v)) +} diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go new file mode 100644 index 000000000000..886663a2daba --- /dev/null +++ b/validator/rpc/handlers_validator_config_test.go @@ -0,0 +1,231 @@ +package rpc + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/OffchainLabs/prysm/v7/config/proposer" + "github.com/OffchainLabs/prysm/v7/testing/assert" + "github.com/OffchainLabs/prysm/v7/testing/require" + "github.com/OffchainLabs/prysm/v7/validator/keymanager/derived" + mocks "github.com/OffchainLabs/prysm/v7/validator/testing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" +) + +// setupConfigServer builds a Server with a derived keymanager holding numKeys +// recovered accounts, and returns the server plus the known validating pubkeys. +func setupConfigServer(t *testing.T, numKeys int) (*Server, [][48]byte) { + ctx := t.Context() + srv := setupServerWithWallet(t) + km, err := srv.validatorService.Keymanager() + require.NoError(t, err) + dr, ok := km.(*derived.Keymanager) + require.Equal(t, true, ok) + require.NoError(t, dr.RecoverAccountsFromMnemonic(ctx, mocks.TestMnemonic, derived.DefaultMnemonicLanguage, "", numKeys)) + keys, err := dr.FetchValidatingPublicKeys(ctx) + require.NoError(t, err) + require.Equal(t, numKeys, len(keys)) + return srv, keys +} + +func postValidatorConfig(t *testing.T, s *Server, rawBody string) *httptest.ResponseRecorder { + req := httptest.NewRequest(http.MethodPost, "/eth/v1/validator/config", bytes.NewBufferString(rawBody)) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + s.SetValidatorConfig(w, req) + return w +} + +func getValidatorConfig(t *testing.T, s *Server, query string) *GetValidatorConfigResponse { + req := httptest.NewRequest(http.MethodGet, "/eth/v1/validator/config"+query, nil) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + s.GetValidatorConfig(w, req) + require.Equal(t, http.StatusOK, w.Code) + resp := &GetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), resp)) + return resp +} + +func TestServer_SetGetValidatorConfig_FullDocument(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + // Seed a read-only default_config so GET surfaces it alongside the per-key fragment. + require.NoError(t, srv.validatorService.SetProposerSettings(t.Context(), &proposer.Settings{ + Version: proposer.SchemaV2, + DefaultConfig: &proposer.Option{FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress("0x1111111111111111111111111111111111111111")}}, + })) + + pk := hexutil.Encode(keys[0][:]) + feeRecipient := "0xabcf8e0d4e9587369b2301d0790347320302cc09" + body := `{"configs":{"` + pk + `":{` + + `"fee_recipient":"` + feeRecipient + `",` + + `"target_gas_limit":"30000000",` + + `"graffiti":"hello world",` + + `"builder":{"enabled":true,"builders":[{"url":"http://relay.example"}],` + + `"max_execution_payment":"1000000","min_bid":"5","builder_boost_factor":"150"}` + + `}}}` + + w := postValidatorConfig(t, srv, body) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.NotNil(t, setResp.Data[pk]) + require.Equal(t, configStatusSet, setResp.Data[pk].Status) + + getResp := getValidatorConfig(t, srv, "") + require.NotNil(t, getResp.Data.DefaultConfig) + require.NotNil(t, getResp.Data.DefaultConfig.FeeRecipient) + require.Equal(t, common.HexToAddress("0x1111111111111111111111111111111111111111"), common.HexToAddress(*getResp.Data.DefaultConfig.FeeRecipient)) + + cfg := getResp.Data.Configs[pk] + require.NotNil(t, cfg) + require.NotNil(t, cfg.FeeRecipient) + require.Equal(t, common.HexToAddress(feeRecipient), common.HexToAddress(*cfg.FeeRecipient)) + require.NotNil(t, cfg.TargetGasLimit) + require.Equal(t, "30000000", *cfg.TargetGasLimit) + require.NotNil(t, cfg.Graffiti) + require.Equal(t, "hello world", *cfg.Graffiti) + require.NotNil(t, cfg.Builder) + require.NotNil(t, cfg.Builder.Enabled) + require.Equal(t, true, *cfg.Builder.Enabled) + require.Equal(t, 1, len(cfg.Builder.Builders)) + require.Equal(t, "http://relay.example", cfg.Builder.Builders[0].Url) + require.NotNil(t, cfg.Builder.MaxExecutionPayment) + require.Equal(t, "1000000", *cfg.Builder.MaxExecutionPayment) + require.NotNil(t, cfg.Builder.MinBid) + require.Equal(t, "5", *cfg.Builder.MinBid) + require.NotNil(t, cfg.Builder.BuilderBoostFactor) + require.Equal(t, "150", *cfg.Builder.BuilderBoostFactor) +} + +func TestServer_SetValidatorConfig_FullReplacementNotMerge(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + feeRecipient := "0xabcf8e0d4e9587369b2301d0790347320302cc09" + + w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"fee_recipient":"`+feeRecipient+`","graffiti":"first"}}}`) + require.Equal(t, http.StatusOK, w.Code) + first := getValidatorConfig(t, srv, "") + require.NotNil(t, first.Data.Configs[pk].Graffiti) + + // Re-POST with only fee_recipient; the whole fragment is replaced, so graffiti disappears. + w = postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"fee_recipient":"`+feeRecipient+`"}}}`) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusSet, setResp.Data[pk].Status) + + second := getValidatorConfig(t, srv, "") + cfg := second.Data.Configs[pk] + require.NotNil(t, cfg) + require.NotNil(t, cfg.FeeRecipient) + require.IsNil(t, cfg.Graffiti) +} + +func TestServer_SetValidatorConfig_EmptyDocumentClearsKey(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + + w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"graffiti":"present"}}}`) + require.Equal(t, http.StatusOK, w.Code) + require.NotNil(t, getValidatorConfig(t, srv, "").Data.Configs[pk]) + + w = postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{}}}`) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusSet, setResp.Data[pk].Status) + + _, present := getValidatorConfig(t, srv, "").Data.Configs[pk] + require.Equal(t, false, present) +} + +func TestServer_SetValidatorConfig_UnknownPubkeyNotFound(t *testing.T) { + srv, _ := setupConfigServer(t, 1) + // 48-byte pubkey never recovered into the keymanager. + unknown := "0x" + "abcdef0011223344556677889900aabbccddeeff00112233445566778899aabbccddeeff001122334455667788990011" + + w := postValidatorConfig(t, srv, `{"configs":{"`+unknown+`":{"graffiti":"x"}}}`) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.NotNil(t, setResp.Data[unknown]) + require.Equal(t, configStatusNotFound, setResp.Data[unknown].Status) + + // Settings are untouched: no per-key config was written. + getResp := getValidatorConfig(t, srv, "") + require.Equal(t, 0, len(getResp.Data.Configs)) +} + +func TestServer_SetValidatorConfig_UnknownJSONFieldRejected(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + + // Unknown field in the per-key config document. + w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"not_a_field":"x"}}}`) + require.Equal(t, http.StatusBadRequest, w.Code) + + // Unknown field in the top-level envelope. + w = postValidatorConfig(t, srv, `{"configs":{},"bogus":true}`) + require.Equal(t, http.StatusBadRequest, w.Code) +} + +func TestServer_SetValidatorConfig_PerKeyErrorsIsolated(t *testing.T) { + srv, keys := setupConfigServer(t, 2) + good := hexutil.Encode(keys[0][:]) + badFee := hexutil.Encode(keys[1][:]) + + body := `{"configs":{` + + `"` + good + `":{"graffiti":"ok"},` + + `"` + badFee + `":{"fee_recipient":"0xnothex"}` + + `}}` + w := postValidatorConfig(t, srv, body) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + + require.Equal(t, configStatusSet, setResp.Data[good].Status) + require.Equal(t, configStatusError, setResp.Data[badFee].Status) + require.NotEqual(t, "", setResp.Data[badFee].Message) + + getResp := getValidatorConfig(t, srv, "") + require.NotNil(t, getResp.Data.Configs[good]) + _, badPresent := getResp.Data.Configs[badFee] + require.Equal(t, false, badPresent) + + // A non-uint target_gas_limit is likewise a per-key error. + w = postValidatorConfig(t, srv, `{"configs":{"`+good+`":{"target_gas_limit":"notanumber"}}}`) + require.Equal(t, http.StatusOK, w.Code) + setResp = &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusError, setResp.Data[good].Status) + require.NotEqual(t, "", setResp.Data[good].Message) +} + +func TestServer_GetValidatorConfig_PubkeyFilter(t *testing.T) { + srv, keys := setupConfigServer(t, 2) + pk0 := hexutil.Encode(keys[0][:]) + pk1 := hexutil.Encode(keys[1][:]) + + w := postValidatorConfig(t, srv, `{"configs":{"`+pk0+`":{"graffiti":"a"},"`+pk1+`":{"graffiti":"b"}}}`) + require.Equal(t, http.StatusOK, w.Code) + + all := getValidatorConfig(t, srv, "") + require.Equal(t, 2, len(all.Data.Configs)) + + filtered := getValidatorConfig(t, srv, "?pubkeys="+pk0) + require.Equal(t, 1, len(filtered.Data.Configs)) + require.NotNil(t, filtered.Data.Configs[pk0]) + _, present := filtered.Data.Configs[pk1] + require.Equal(t, false, present) +} + +func TestServer_SetValidatorConfig_ValidatorServiceNil(t *testing.T) { + s := &Server{} + w := postValidatorConfig(t, s, `{"configs":{}}`) + assert.Equal(t, http.StatusServiceUnavailable, w.Code) +} diff --git a/validator/rpc/server.go b/validator/rpc/server.go index a718799cc81f..0f7fc30eec57 100644 --- a/validator/rpc/server.go +++ b/validator/rpc/server.go @@ -7,6 +7,7 @@ import ( "net/http" "path/filepath" "strings" + "sync" "time" "github.com/OffchainLabs/prysm/v7/api" @@ -80,6 +81,7 @@ type Server struct { walletDir string jwtSecret []byte grpcHeaders []string + proposerSettingsLock sync.Mutex } // NewServer instantiates a new HTTP server. @@ -198,6 +200,8 @@ func (s *Server) InitializeRoutes() error { s.router.HandleFunc("GET /eth/v1/validator/{pubkey}/graffiti", s.GetGraffiti) s.router.HandleFunc("POST /eth/v1/validator/{pubkey}/graffiti", s.SetGraffiti) s.router.HandleFunc("DELETE /eth/v1/validator/{pubkey}/graffiti", s.DeleteGraffiti) + s.router.HandleFunc("GET /eth/v1/validator/config", s.GetValidatorConfig) + s.router.HandleFunc("POST /eth/v1/validator/config", s.SetValidatorConfig) // auth endpoint s.router.HandleFunc("GET "+api.WebUrlPrefix+"initialize", s.Initialize) diff --git a/validator/rpc/structs.go b/validator/rpc/structs.go index 77bfabff8f94..c4436b985171 100644 --- a/validator/rpc/structs.go +++ b/validator/rpc/structs.go @@ -109,6 +109,56 @@ type GraffitiData struct { Graffiti string `json:"graffiti"` } +// Validator config keymanager api (keymanager-APIs #87). All integers are +// decimal strings and all byte values are 0x-prefixed hex, per keymanager conventions. +type GetValidatorConfigResponse struct { + Data *ValidatorConfigData `json:"data"` +} + +type ValidatorConfigData struct { + DefaultConfig *ValidatorConfig `json:"default_config,omitempty"` + Configs map[string]*ValidatorConfig `json:"configs"` +} + +type SetValidatorConfigRequest struct { + Configs map[string]*ValidatorConfig `json:"configs"` +} + +type SetValidatorConfigResponse struct { + Data map[string]*ValidatorConfigStatus `json:"data"` +} + +type ValidatorConfigStatus struct { + Status string `json:"status"` + Message string `json:"message,omitempty"` +} + +type ValidatorConfig struct { + FeeRecipient *string `json:"fee_recipient,omitempty"` + TargetGasLimit *string `json:"target_gas_limit,omitempty"` + Graffiti *string `json:"graffiti,omitempty"` + Builder *BuilderConfigJson `json:"builder,omitempty"` +} + +type BuilderConfigJson struct { + Enabled *bool `json:"enabled,omitempty"` + Builders []*BuilderEntryJson `json:"builders,omitempty"` + MaxExecutionPayment *string `json:"max_execution_payment,omitempty"` + MinBid *string `json:"min_bid,omitempty"` + BuilderBoostFactor *string `json:"builder_boost_factor,omitempty"` + Proxy *string `json:"proxy,omitempty"` +} + +type BuilderEntryJson struct { + Url string `json:"url"` + Pubkey *string `json:"pubkey,omitempty"` + AuthData *string `json:"auth_data,omitempty"` + Proxy *string `json:"proxy,omitempty"` + MaxExecutionPayment *string `json:"max_execution_payment,omitempty"` + MinBid *string `json:"min_bid,omitempty"` + BuilderBoostFactor *string `json:"builder_boost_factor,omitempty"` +} + type BeaconStatusResponse struct { BeaconNodeEndpoint string `json:"beacon_node_endpoint"` Connected bool `json:"connected"` From 204cd187dd04debdd54458fc2c3c331510e98c46 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 20 Jul 2026 09:51:47 -0500 Subject: [PATCH 02/19] poc additional builder changes --- beacon-chain/builder/service.go | 20 ++++++-------- beacon-chain/builder/service_gloas_test.go | 14 +++++++--- beacon-chain/builder/testing/mock.go | 2 +- .../rpc/eth/validator/builder_preferences.go | 2 ++ .../prysm/v1alpha1/validator/proposer_bid.go | 6 ++--- .../validator/proposer_bid_builder_test.go | 6 ++++- .../v1alpha1/validator/proposer_gloas.go | 18 +++++++------ proto/prysm/v1alpha1/gloas_builder_api.pb.go | 27 ++++++++++++------- proto/prysm/v1alpha1/gloas_builder_api.proto | 3 +++ .../client/beacon-api/builder_preferences.go | 2 ++ validator/client/request_auth.go | 27 +++++++++---------- validator/client/validator.go | 24 ++++++++--------- 12 files changed, 86 insertions(+), 65 deletions(-) diff --git a/beacon-chain/builder/service.go b/beacon-chain/builder/service.go index ad7f4c0a7986..cf1cf5b83878 100644 --- a/beacon-chain/builder/service.go +++ b/beacon-chain/builder/service.go @@ -29,7 +29,7 @@ type BlockBuilder interface { SubmitBlindedBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) (interfaces.ExecutionData, v1.BlobsBundler, error) SubmitBlindedBlockPostFulu(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock) error GetHeader(ctx context.Context, slot primitives.Slot, parentHash [32]byte, pubKey [48]byte) (builder.SignedBid, error) - GetExecutionPayloadBid(ctx context.Context, slot primitives.Slot, parentHash, parentRoot [32]byte, proposerPubkey [48]byte, auths []*ethpb.SignedRequestAuthV1) ([]PayloadBid, error) + GetExecutionPayloadBid(ctx context.Context, slot primitives.Slot, parentHash, parentRoot [32]byte, proposerPubkey [48]byte, authsByURL map[string]*ethpb.SignedRequestAuthV1) ([]PayloadBid, error) SubmitSignedBeaconBlock(ctx context.Context, builderURL string, block interfaces.ReadOnlySignedBeaconBlock) error SubmitBuilderPreferences(ctx context.Context, validatorPubkey [48]byte, req *ethpb.BuilderPreferencesRequestV1) error RegisterValidator(ctx context.Context, reg []*ethpb.SignedValidatorRegistrationV1) error @@ -163,19 +163,15 @@ func (s *Service) SubmitBlindedBlockPostFulu(ctx context.Context, b interfaces.R } // Builders are queried concurrently, a failing builder drops only its own bid. -func (s *Service) GetExecutionPayloadBid(ctx context.Context, slot primitives.Slot, parentHash, parentRoot [32]byte, proposerPubkey [48]byte, auths []*ethpb.SignedRequestAuthV1) ([]PayloadBid, error) { +func (s *Service) GetExecutionPayloadBid(ctx context.Context, slot primitives.Slot, parentHash, parentRoot [32]byte, proposerPubkey [48]byte, authsByURL map[string]*ethpb.SignedRequestAuthV1) ([]PayloadBid, error) { ctx, span := trace.StartSpan(ctx, "builder.GetExecutionPayloadBid") defer span.End() - byURL := make(map[string]*ethpb.SignedRequestAuthV1, len(auths)) - urls := make([]string, 0, len(auths)) - for _, a := range auths { - url := string(a.GetMessage().GetData()) - if url == "" { - continue - } - if _, ok := byURL[url]; !ok { - byURL[url] = a + // Per builder-specs 5078eab the signed auth data is opaque, so the builder URL + // is provided explicitly by the caller rather than decoded from the auth. + urls := make([]string, 0, len(authsByURL)) + for url := range authsByURL { + if url != "" { urls = append(urls, url) } } @@ -197,7 +193,7 @@ func (s *Service) GetExecutionPayloadBid(ctx context.Context, slot primitives.Sl log.WithError(err).WithField("builder", logs.MaskCredentialsLogging(url)).Warn("Could not get builder client") return } - bid, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, proposerPubkey, byURL[url]) + bid, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, proposerPubkey, authsByURL[url]) if err != nil { log.WithError(err).WithField("builder", logs.MaskCredentialsLogging(url)).Warn("Could not get builder execution payload bid") return diff --git a/beacon-chain/builder/service_gloas_test.go b/beacon-chain/builder/service_gloas_test.go index d7983c799c30..989e27242416 100644 --- a/beacon-chain/builder/service_gloas_test.go +++ b/beacon-chain/builder/service_gloas_test.go @@ -54,6 +54,14 @@ func newMultiplexService(t *testing.T, clients map[string]*fakeBuilderClient) *S return s } +func authsByURL(urls ...string) map[string]*eth.SignedRequestAuthV1 { + m := make(map[string]*eth.SignedRequestAuthV1, len(urls)) + for _, u := range urls { + m[u] = authFor(u) + } + return m +} + func TestGetExecutionPayloadBid_FanOutAndDedup(t *testing.T) { clients := map[string]*fakeBuilderClient{ "http://a": {url: "http://a", bid: bidWithValue(100)}, @@ -61,8 +69,7 @@ func TestGetExecutionPayloadBid_FanOutAndDedup(t *testing.T) { } s := newMultiplexService(t, clients) - auths := []*eth.SignedRequestAuthV1{authFor("http://a"), authFor("http://b"), authFor("http://a")} - bids, err := s.GetExecutionPayloadBid(t.Context(), 1, [32]byte{}, [32]byte{}, [48]byte{}, auths) + bids, err := s.GetExecutionPayloadBid(t.Context(), 1, [32]byte{}, [32]byte{}, [48]byte{}, authsByURL("http://a", "http://b")) require.NoError(t, err) require.Equal(t, 2, len(bids)) @@ -83,8 +90,7 @@ func TestGetExecutionPayloadBid_SkipsErrorsAndNil(t *testing.T) { s := newMultiplexService(t, clients) // http://nodial has no client; dialing it fails and is skipped. - auths := []*eth.SignedRequestAuthV1{authFor("http://ok"), authFor("http://err"), authFor("http://none"), authFor("http://nodial")} - bids, err := s.GetExecutionPayloadBid(t.Context(), 1, [32]byte{}, [32]byte{}, [48]byte{}, auths) + bids, err := s.GetExecutionPayloadBid(t.Context(), 1, [32]byte{}, [32]byte{}, [48]byte{}, authsByURL("http://ok", "http://err", "http://none", "http://nodial")) require.NoError(t, err) require.Equal(t, 1, len(bids)) require.Equal(t, "http://ok", bids[0].BuilderURL) diff --git a/beacon-chain/builder/testing/mock.go b/beacon-chain/builder/testing/mock.go index 527e15d9086d..4320dce25b43 100644 --- a/beacon-chain/builder/testing/mock.go +++ b/beacon-chain/builder/testing/mock.go @@ -134,7 +134,7 @@ func (s *MockBuilderService) SubmitBlindedBlockPostFulu(_ context.Context, _ int } // GetExecutionPayloadBid for mocking. -func (s *MockBuilderService) GetExecutionPayloadBid(_ context.Context, _ primitives.Slot, _, _ [32]byte, _ [48]byte, _ []*ethpb.SignedRequestAuthV1) ([]beaconbuilder.PayloadBid, error) { +func (s *MockBuilderService) GetExecutionPayloadBid(_ context.Context, _ primitives.Slot, _, _ [32]byte, _ [48]byte, _ map[string]*ethpb.SignedRequestAuthV1) ([]beaconbuilder.PayloadBid, error) { if s.PayloadBids != nil { return s.PayloadBids, s.ErrGetExecutionPayloadBid } diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index ce68e4296278..3c0c68d72365 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -19,6 +19,7 @@ type produceBlockV4Request struct { } type builderPreferenceJson struct { + Url string `json:"url"` SignedRequestAuth *signedRequestAuthJson `json:"signed_request_auth"` MaxExecutionPayment string `json:"max_execution_payment"` MinBid string `json:"min_bid,omitempty"` @@ -80,6 +81,7 @@ func (p *builderPreferenceJson) toConsensus(i int) (*eth.BuilderPreferenceV1, er return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.signature is not valid hex", i) } out := ð.BuilderPreferenceV1{ + Url: p.Url, Request: ð.BuilderPreferencesRequestV1{ Preferences: ð.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(maxPayment)}, Auth: ð.SignedRequestAuthV1{ diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index 11612c388ed1..c2360ce6ee6e 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -182,16 +182,16 @@ type builderBidQuery struct { feeRecipient []byte parentGasLimit uint64 targetGasLimit uint64 - auths []*ethpb.SignedRequestAuthV1 + authsByURL map[string]*ethpb.SignedRequestAuthV1 } func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state.BeaconState, q *builderBidQuery) (*ethpb.SignedExecutionPayloadBid, string) { - if vs.BlockBuilder == nil || len(q.auths) == 0 { + if vs.BlockBuilder == nil || len(q.authsByURL) == 0 { return nil, "" } ctx, cancel := context.WithTimeout(ctx, builderBidTimeout) defer cancel() - bids, err := vs.BlockBuilder.GetExecutionPayloadBid(ctx, q.slot, q.parentHash, q.parentRoot, q.pubkey, q.auths) + bids, err := vs.BlockBuilder.GetExecutionPayloadBid(ctx, q.slot, q.parentHash, q.parentRoot, q.pubkey, q.authsByURL) if err != nil { builderGetPayloadMissCount.Inc() log.WithError(err).Error("Could not get builder execution payload bid") diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go index b1821d577a5e..28d6da9bfedf 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go @@ -260,12 +260,16 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { return &fakeBidVerifier{} } query := func(auths []*ethpb.SignedRequestAuthV1) *builderBidQuery { + byURL := make(map[string]*ethpb.SignedRequestAuthV1, len(auths)) + for _, a := range auths { + byURL[string(a.GetMessage().GetData())] = a + } return &builderBidQuery{ slot: slot, parentRoot: parentRoot, parentHash: parentHash, pubkey: pubkey, - auths: auths, + authsByURL: byURL, } } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go index 56ab468b996e..6ddcb9af806c 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go @@ -56,8 +56,8 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea log.WithError(glErr).Error("Could not get parent gas limit for builder bid request") default: pubkey := val.PublicKey() - var auths []*ethpb.SignedRequestAuthV1 - auths, prefs = builderAuthsAndPrefs(builderPreferences) + var authsByURL map[string]*ethpb.SignedRequestAuthV1 + authsByURL, prefs = builderAuthsAndPrefs(builderPreferences) pref := vs.proposerPreferenceForProposal(ctx, head, sBlk.Block().Slot(), sBlk.Block().ProposerIndex()) feeRecipient := pref.FeeRecipientOrDefault() builderBid, builderURL = vs.getBuilderExecutionPayloadBid(ctx, head, &builderBidQuery{ @@ -69,7 +69,7 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea feeRecipient: feeRecipient[:], parentGasLimit: parentGasLimit, targetGasLimit: pref.GasLimitOr(parentGasLimit), - auths: auths, + authsByURL: authsByURL, }) } } @@ -124,15 +124,17 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea // the block request. The effective preferences use the first valid entry as the // proposal baseline. The beacon node enforces the caps locally during bid // selection; communicating preferences to the builder is left to the bid request. -func builderAuthsAndPrefs(prefs []*ethpb.BuilderPreferenceV1) ([]*ethpb.SignedRequestAuthV1, bidPreferences) { - auths := make([]*ethpb.SignedRequestAuthV1, 0, len(prefs)) +func builderAuthsAndPrefs(prefs []*ethpb.BuilderPreferenceV1) (map[string]*ethpb.SignedRequestAuthV1, bidPreferences) { + authsByURL := make(map[string]*ethpb.SignedRequestAuthV1, len(prefs)) bp := bidPreferences{boostFactor: neutralBuilderBoostFactor} baselineSet := false for _, p := range prefs { - if p == nil || p.Request == nil || p.Request.Auth == nil { + if p == nil || p.Request == nil || p.Request.Auth == nil || p.Url == "" { continue } - auths = append(auths, p.Request.Auth) + if _, ok := authsByURL[p.Url]; !ok { + authsByURL[p.Url] = p.Request.Auth + } if !baselineSet { bp.maxPayment = uint64(p.Request.Preferences.GetMaxExecutionPayment()) if p.MinBid != nil { @@ -144,5 +146,5 @@ func builderAuthsAndPrefs(prefs []*ethpb.BuilderPreferenceV1) ([]*ethpb.SignedRe baselineSet = true } } - return auths, bp + return authsByURL, bp } diff --git a/proto/prysm/v1alpha1/gloas_builder_api.pb.go b/proto/prysm/v1alpha1/gloas_builder_api.pb.go index bfb3a8cdcd7a..ef12acddceee 100755 --- a/proto/prysm/v1alpha1/gloas_builder_api.pb.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.pb.go @@ -228,6 +228,7 @@ type BuilderPreferenceV1 struct { Request *BuilderPreferencesRequestV1 `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` MinBid *github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei `protobuf:"varint,2,opt,name=min_bid,json=minBid,proto3,oneof" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei"` BuilderBoostFactor *uint64 `protobuf:"varint,3,opt,name=builder_boost_factor,json=builderBoostFactor,proto3,oneof" json:"builder_boost_factor,omitempty"` + Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -283,6 +284,13 @@ func (x *BuilderPreferenceV1) GetBuilderBoostFactor() uint64 { return 0 } +func (x *BuilderPreferenceV1) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + var File_proto_prysm_v1alpha1_gloas_builder_api_proto protoreflect.FileDescriptor var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ @@ -328,7 +336,7 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x75, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xa3, 0x02, 0x0a, 0x13, + 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x56, 0x31, 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, @@ -344,14 +352,15 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, - 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, - 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, - 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, + 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x42, 0x0a, + 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index ae031f143c2d..7d6c66346db3 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -54,4 +54,7 @@ message BuilderPreferenceV1 { "github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei" ]; optional uint64 builder_boost_factor = 3; + // url identifies the builder. Per builder-specs 5078eab the signed request auth + // data is opaque, so the URL is no longer recoverable from it and is carried here. + string url = 4; } diff --git a/validator/client/beacon-api/builder_preferences.go b/validator/client/beacon-api/builder_preferences.go index 88b445407c67..5b2114d509c2 100644 --- a/validator/client/beacon-api/builder_preferences.go +++ b/validator/client/beacon-api/builder_preferences.go @@ -14,6 +14,7 @@ type builderPreferencesBody struct { } type builderPreferenceReq struct { + Url string `json:"url"` SignedRequestAuth *signedRequestAuthReq `json:"signed_request_auth"` MaxExecutionPayment string `json:"max_execution_payment"` MinBid string `json:"min_bid,omitempty"` @@ -38,6 +39,7 @@ func marshalBuilderPreferences(prefs []*ethpb.BuilderPreferenceV1) ([]byte, erro continue } req := &builderPreferenceReq{ + Url: p.Url, MaxExecutionPayment: strconv.FormatUint(uint64(p.Request.Preferences.GetMaxExecutionPayment()), 10), SignedRequestAuth: &signedRequestAuthReq{ Message: &requestAuthReq{ diff --git a/validator/client/request_auth.go b/validator/client/request_auth.go index 8f244c65e64d..8a5e90e345ea 100644 --- a/validator/client/request_auth.go +++ b/validator/client/request_auth.go @@ -20,18 +20,6 @@ type requestAuthKey struct { relay string } -func (v *validator) builderRequestAuthsForSlot(pk pubkey, slot primitives.Slot) []*ethpb.SignedRequestAuthV1 { - v.signedRequestAuthsLock.Lock() - defer v.signedRequestAuthsLock.Unlock() - var auths []*ethpb.SignedRequestAuthV1 - for k, signed := range v.signedRequestAuths { - if k.pk == pk && k.slot == slot { - auths = append(auths, signed) - } - } - return auths -} - func (v *validator) pruneSignedRequestAuths(slot primitives.Slot) { v.signedRequestAuthsLock.Lock() defer v.signedRequestAuthsLock.Unlock() @@ -42,7 +30,10 @@ func (v *validator) pruneSignedRequestAuths(slot primitives.Slot) { } } -func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKeymanager, pk pubkey, relay string, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, error) { +// signRequestAuthCached signs a request auth for the (pk, relay, slot) tuple. Per +// builder-specs 5078eab the signed data is the opaque authData agreed with the +// builder out of band, not the builder URL; the URL is keyed separately. +func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKeymanager, pk pubkey, relay string, authData []byte, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, error) { key := requestAuthKey{pk: pk, slot: slot, relay: relay} v.signedRequestAuthsLock.Lock() signed, ok := v.signedRequestAuths[key] @@ -50,7 +41,7 @@ func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKe if ok { return signed, nil } - signed, err := v.signRequestAuth(ctx, km, pk, ðpb.RequestAuthV1{Data: []byte(relay), Slot: slot}) + signed, err := v.signRequestAuth(ctx, km, pk, ðpb.RequestAuthV1{Data: authData, Slot: slot}) if err != nil { return nil, err } @@ -63,6 +54,14 @@ func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKe return signed, nil } +// signedRequestAuthFor returns the cached signed auth for the (pk, relay, slot) tuple. +func (v *validator) signedRequestAuthFor(pk pubkey, relay string, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, bool) { + v.signedRequestAuthsLock.Lock() + defer v.signedRequestAuthsLock.Unlock() + signed, ok := v.signedRequestAuths[requestAuthKey{pk: pk, slot: slot, relay: relay}] + return signed, ok +} + // Domain is fork-independent: compute_domain(DOMAIN_REQUEST_AUTH) with genesis fork version and zero genesis validators root. func (v *validator) signRequestAuth( ctx context.Context, diff --git a/validator/client/validator.go b/validator/client/validator.go index bd08df2ec688..095de17f1d73 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1353,6 +1353,7 @@ func (v *validator) submittedPrefSlotsCount() int { // preferences to submit for it, with per-entry overrides already applied. type builderTarget struct { url string + authData []byte maxPayment uint64 minBid *uint64 boostFactor *uint64 @@ -1391,7 +1392,7 @@ func (v *validator) builderTargetsForKey(pk pubkey) []builderTarget { if e == nil || e.URL == "" { continue } - t := builderTarget{url: e.URL, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost} + t := builderTarget{url: e.URL, authData: e.AuthData, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost} if e.MaxExecutionPayment != nil { t.maxPayment = uint64(*e.MaxExecutionPayment) } @@ -1445,7 +1446,7 @@ func (v *validator) warmBuilderRequestAuthsForDuties(ctx context.Context, km key continue } for _, t := range targets { - if _, err := v.signRequestAuthCached(ctx, km, pk, t.url, proposalSlot); err != nil { + if _, err := v.signRequestAuthCached(ctx, km, pk, t.url, t.authData, proposalSlot); err != nil { log.WithError(err).Warn("Failed to sign builder request auth") } } @@ -1457,17 +1458,10 @@ func (v *validator) warmBuilderRequestAuthsForDuties(ctx context.Context, km key // inline on the block request for pk's proposal at slot, pairing each cached // signed auth with its resolved caps. func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Slot) []*ethpb.BuilderPreferenceV1 { - auths := v.builderRequestAuthsForSlot(pk, slot) - if len(auths) == 0 { - return nil - } - capsByURL := make(map[string]builderTarget) - for _, t := range v.builderTargetsForKey(pk) { - capsByURL[t.url] = t - } - prefs := make([]*ethpb.BuilderPreferenceV1, 0, len(auths)) - for _, auth := range auths { - t, ok := capsByURL[string(auth.GetMessage().GetData())] + targets := v.builderTargetsForKey(pk) + prefs := make([]*ethpb.BuilderPreferenceV1, 0, len(targets)) + for _, t := range targets { + auth, ok := v.signedRequestAuthFor(pk, t.url, slot) if !ok { continue } @@ -1476,6 +1470,7 @@ func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Sl Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(t.maxPayment)}, Auth: auth, }, + Url: t.url, BuilderBoostFactor: t.boostFactor, } if t.minBid != nil { @@ -1484,6 +1479,9 @@ func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Sl } prefs = append(prefs, p) } + if len(prefs) == 0 { + return nil + } return prefs } From 43b7544d9cf780b488fdf6c869d82530237609eb Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 20 Jul 2026 11:31:46 -0500 Subject: [PATCH 03/19] fixing auth bug --- validator/client/BUILD.bazel | 1 + validator/client/request_auth.go | 23 ++--- validator/client/request_auth_test.go | 124 ++++++++++++++++++++++++++ validator/client/validator.go | 2 +- 4 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 validator/client/request_auth_test.go diff --git a/validator/client/BUILD.bazel b/validator/client/BUILD.bazel index a3b8d5625b92..8e37847e58c3 100644 --- a/validator/client/BUILD.bazel +++ b/validator/client/BUILD.bazel @@ -129,6 +129,7 @@ go_test( "propose_gloas_test.go", "propose_test.go", "registration_test.go", + "request_auth_test.go", "runner_test.go", "service_test.go", "slashing_protection_interchange_test.go", diff --git a/validator/client/request_auth.go b/validator/client/request_auth.go index 8a5e90e345ea..54961f69a702 100644 --- a/validator/client/request_auth.go +++ b/validator/client/request_auth.go @@ -14,10 +14,13 @@ import ( "github.com/pkg/errors" ) +// authData is part of the key so a cached signature always covers exactly the +// data configured for the builder; rotating auth_data can never serve a stale auth. type requestAuthKey struct { - pk pubkey - slot primitives.Slot - relay string + pk pubkey + slot primitives.Slot + relay string + authData string } func (v *validator) pruneSignedRequestAuths(slot primitives.Slot) { @@ -30,11 +33,11 @@ func (v *validator) pruneSignedRequestAuths(slot primitives.Slot) { } } -// signRequestAuthCached signs a request auth for the (pk, relay, slot) tuple. Per -// builder-specs 5078eab the signed data is the opaque authData agreed with the -// builder out of band, not the builder URL; the URL is keyed separately. +// signRequestAuthCached signs a request auth for the (pk, relay, authData, slot) +// tuple. Per builder-specs 5078eab the signed data is the opaque authData agreed +// with the builder out of band, not the builder URL. func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKeymanager, pk pubkey, relay string, authData []byte, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, error) { - key := requestAuthKey{pk: pk, slot: slot, relay: relay} + key := requestAuthKey{pk: pk, slot: slot, relay: relay, authData: string(authData)} v.signedRequestAuthsLock.Lock() signed, ok := v.signedRequestAuths[key] v.signedRequestAuthsLock.Unlock() @@ -54,11 +57,11 @@ func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKe return signed, nil } -// signedRequestAuthFor returns the cached signed auth for the (pk, relay, slot) tuple. -func (v *validator) signedRequestAuthFor(pk pubkey, relay string, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, bool) { +// signedRequestAuthFor returns the cached signed auth for the (pk, relay, authData, slot) tuple. +func (v *validator) signedRequestAuthFor(pk pubkey, relay string, authData []byte, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, bool) { v.signedRequestAuthsLock.Lock() defer v.signedRequestAuthsLock.Unlock() - signed, ok := v.signedRequestAuths[requestAuthKey{pk: pk, slot: slot, relay: relay}] + signed, ok := v.signedRequestAuths[requestAuthKey{pk: pk, slot: slot, relay: relay, authData: string(authData)}] return signed, ok } diff --git a/validator/client/request_auth_test.go b/validator/client/request_auth_test.go new file mode 100644 index 000000000000..df853c71b359 --- /dev/null +++ b/validator/client/request_auth_test.go @@ -0,0 +1,124 @@ +package client + +import ( + "testing" + + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + "github.com/OffchainLabs/prysm/v7/config/proposer" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/testing/require" + "google.golang.org/protobuf/proto" +) + +func cachedAuth(data []byte, slot primitives.Slot) *ethpb.SignedRequestAuthV1 { + return ðpb.SignedRequestAuthV1{Message: ðpb.RequestAuthV1{Data: data, Slot: slot}} +} + +func settingsWithBuilders(pk [fieldparams.BLSPubkeyLength]byte, entries ...*proposer.BuilderEntry) *proposer.Settings { + return &proposer.Settings{ + Version: proposer.SchemaV2, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + pk: {BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), Builders: entries}}, + }, + } +} + +// A cached auth signed over rotated-away auth_data must never be attached to a proposal. +func TestBuildBuilderPreferencesForSlot_StaleAuthNotServedAfterRotation(t *testing.T) { + pk := [fieldparams.BLSPubkeyLength]byte{1} + slot := primitives.Slot(10) + url := "https://builder.example" + + v := validator{ + proposerSettings: settingsWithBuilders(pk, &proposer.BuilderEntry{URL: url, AuthData: []byte("B")}), + signedRequestAuths: map[requestAuthKey]*ethpb.SignedRequestAuthV1{ + {pk: pk, slot: slot, relay: url, authData: "A"}: cachedAuth([]byte("A"), slot), + }, + } + + require.Equal(t, 0, len(v.buildBuilderPreferencesForSlot(pk, slot))) + + v.signedRequestAuths[requestAuthKey{pk: pk, slot: slot, relay: url, authData: "B"}] = cachedAuth([]byte("B"), slot) + prefs := v.buildBuilderPreferencesForSlot(pk, slot) + require.Equal(t, 1, len(prefs)) + require.Equal(t, url, prefs[0].Url) + require.DeepEqual(t, []byte("B"), prefs[0].Request.Auth.Message.Data) +} + +// Duplicate URLs with distinct auth_data must each pair with their own signed auth. +func TestBuildBuilderPreferencesForSlot_DuplicateURLDistinctAuthData(t *testing.T) { + pk := [fieldparams.BLSPubkeyLength]byte{2} + slot := primitives.Slot(7) + url := "https://builder.example" + + v := validator{ + proposerSettings: settingsWithBuilders(pk, + &proposer.BuilderEntry{URL: url, AuthData: []byte("A")}, + &proposer.BuilderEntry{URL: url, AuthData: []byte("B")}, + ), + signedRequestAuths: map[requestAuthKey]*ethpb.SignedRequestAuthV1{ + {pk: pk, slot: slot, relay: url, authData: "A"}: cachedAuth([]byte("A"), slot), + {pk: pk, slot: slot, relay: url, authData: "B"}: cachedAuth([]byte("B"), slot), + }, + } + + prefs := v.buildBuilderPreferencesForSlot(pk, slot) + require.Equal(t, 2, len(prefs)) + require.DeepEqual(t, []byte("A"), prefs[0].Request.Auth.Message.Data) + require.DeepEqual(t, []byte("B"), prefs[1].Request.Auth.Message.Data) +} + +// Drives the real sign path (as warmBuilderRequestAuthsForDuties does) so a key-shape +// mismatch between signRequestAuthCached and signedRequestAuthFor cannot go unnoticed. +func TestSignRequestAuthCached_RotationRoundTrip(t *testing.T) { + kp := randKeypair(t) + km := newMockKeymanager(t, kp) + ctx := t.Context() + slot := primitives.Slot(20) + url := "https://builder.example" + + v := validator{proposerSettings: settingsWithBuilders(kp.pub, &proposer.BuilderEntry{URL: url, AuthData: []byte("A")})} + warm := func() { + for _, tgt := range v.builderTargetsForKey(kp.pub) { + _, err := v.signRequestAuthCached(ctx, km, kp.pub, tgt.url, tgt.authData, slot) + require.NoError(t, err) + } + } + + warm() + prefs := v.buildBuilderPreferencesForSlot(kp.pub, slot) + require.Equal(t, 1, len(prefs)) + require.DeepEqual(t, []byte("A"), prefs[0].Request.Auth.Message.Data) + + // Rotate auth_data; the cached A-auth must not be served, and re-warming must re-sign. + v.proposerSettings = settingsWithBuilders(kp.pub, &proposer.BuilderEntry{URL: url, AuthData: []byte("B")}) + require.Equal(t, 0, len(v.buildBuilderPreferencesForSlot(kp.pub, slot))) + + warm() + prefs = v.buildBuilderPreferencesForSlot(kp.pub, slot) + require.Equal(t, 1, len(prefs)) + require.DeepEqual(t, []byte("B"), prefs[0].Request.Auth.Message.Data) + require.Equal(t, 2, len(v.signedRequestAuths)) + + v.pruneSignedRequestAuths(slot + 1) + require.Equal(t, 0, len(v.signedRequestAuths)) +} + +func TestSignedRequestAuthFor_KeyIncludesAuthData(t *testing.T) { + pk := [fieldparams.BLSPubkeyLength]byte{3} + slot := primitives.Slot(4) + url := "https://builder.example" + + v := validator{ + signedRequestAuths: map[requestAuthKey]*ethpb.SignedRequestAuthV1{ + {pk: pk, slot: slot, relay: url, authData: "A"}: cachedAuth([]byte("A"), slot), + }, + } + + _, ok := v.signedRequestAuthFor(pk, url, []byte("B"), slot) + require.Equal(t, false, ok) + got, ok := v.signedRequestAuthFor(pk, url, []byte("A"), slot) + require.Equal(t, true, ok) + require.DeepEqual(t, []byte("A"), got.Message.Data) +} diff --git a/validator/client/validator.go b/validator/client/validator.go index 095de17f1d73..802217c1add7 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1461,7 +1461,7 @@ func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Sl targets := v.builderTargetsForKey(pk) prefs := make([]*ethpb.BuilderPreferenceV1, 0, len(targets)) for _, t := range targets { - auth, ok := v.signedRequestAuthFor(pk, t.url, slot) + auth, ok := v.signedRequestAuthFor(pk, t.url, t.authData, slot) if !ok { continue } From ef15d459f7a9f02b8c2e6c08ddf19213508d0ea9 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 20 Jul 2026 14:36:52 -0500 Subject: [PATCH 04/19] fixing implicit builder activation and also fixing inheritance of settings --- .../james-prysm_validator-config-endpoint.md | 13 +- config/proposer/BUILD.bazel | 5 +- .../proposer/effective_builder_config_test.go | 122 +++++++++++ config/proposer/settings.go | 96 ++++++++- config/proposer/settings_test.go | 74 ++++++- .../validator-client/keymanager.pb.go | 202 +++++++++--------- .../validator-client/keymanager.proto | 6 +- validator/client/request_auth_test.go | 114 ++++++++++ validator/client/validator.go | 66 +++--- validator/client/validator_test.go | 12 +- validator/rpc/handlers_validator_config.go | 11 +- .../rpc/handlers_validator_config_test.go | 31 +++ 12 files changed, 594 insertions(+), 158 deletions(-) create mode 100644 config/proposer/effective_builder_config_test.go diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index 068da6bea866..9d58f4a8d5d8 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -2,7 +2,18 @@ - Add `GET`/`POST /eth/v1/validator/config` keymanager endpoints (keymanager-APIs #87), managing fee recipient, target gas limit, graffiti, and per-key builder preferences (builder list, `min_bid`, `max_execution_payment`, `builder_boost_factor`, proxy) as one atomic per-key document. POST replaces a key's fragment, an empty document clears it, and each key is applied independently with a `set`/`not_found`/`error` status. - Wire per-key `builder_boost_factor` and `min_bid` into Gloas builder-API bid selection, and `builder_boost_factor` into the pre-Gloas `builder_boost_factor` block-production query. +- Accept `POST /eth/v4/validator/blocks/{slot}` carrying inline per-builder preferences (beacon-APIs #625 JIT model). ### Changed -- Proposer builder config `enabled` is now presence-tracked so an unset value inherits from `default_config` instead of defaulting to disabled. +- Proposer builder config `enabled` and `max_execution_payment` are now presence-tracked: unset values inherit instead of defaulting (explicit `max_execution_payment: 0` still means trustless-only). +- Per-key builder config now resolves with field-level inheritance from `default_config`: unset fields inherit the default's values, and a present `builders`/`relays` list replaces the default's. Per-key builder settings no longer require a per-key fee recipient for validator registration (the default fee recipient is inherited). +- Builder preferences now travel inline with the block request (JIT) instead of being pushed ahead to a beacon-node cache; request auths sign the opaque per-builder `auth_data` (builder-specs #165) with the builder URL carried explicitly. + +### Fixed + +- Normalize legacy (pre-v2) proposer settings on load and at the v2 upgrade: wire-absent builder `enabled` becomes an explicit `false` (a previously disabled per-key builder section can never silently activate through inheritance), and the filesystem backend's spurious explicit-0 `max_execution_payment` is cleared so both database backends decode legacy data identically. + +### Removed + +- Remove the `SubmitBuilderPreferences` internal gRPC RPC and the beacon-node builder-preference caches; `BlockRequest` field `builder_request_auths` is replaced by `builder_preferences`. diff --git a/config/proposer/BUILD.bazel b/config/proposer/BUILD.bazel index 093fb80ed8cb..504079e45527 100644 --- a/config/proposer/BUILD.bazel +++ b/config/proposer/BUILD.bazel @@ -24,7 +24,10 @@ go_library( go_test( name = "go_default_test", - srcs = ["settings_test.go"], + srcs = [ + "effective_builder_config_test.go", + "settings_test.go", + ], embed = [":go_default_library"], deps = [ "//config/fieldparams:go_default_library", diff --git a/config/proposer/effective_builder_config_test.go b/config/proposer/effective_builder_config_test.go new file mode 100644 index 000000000000..ba429e1efb39 --- /dev/null +++ b/config/proposer/effective_builder_config_test.go @@ -0,0 +1,122 @@ +package proposer + +import ( + "testing" + + "github.com/OffchainLabs/prysm/v7/consensus-types/validator" + "github.com/OffchainLabs/prysm/v7/testing/require" + "google.golang.org/protobuf/proto" +) + +// These cases mirror the test vectors proposed upstream on keymanager-APIs #87 +// for builder-config inheritance granularity. +func TestEffectiveBuilderConfig(t *testing.T) { + entryA := &BuilderEntry{URL: "https://a"} + entryB := &BuilderEntry{URL: "https://b"} + entryC := &BuilderEntry{URL: "https://c"} + + t.Run("nil per-key returns default", func(t *testing.T) { + def := &BuilderConfig{Enabled: proto.Bool(true)} + require.Equal(t, def, EffectiveBuilderConfig(nil, def)) + }) + t.Run("nil default returns per-key", func(t *testing.T) { + perKey := &BuilderConfig{Enabled: proto.Bool(true)} + require.Equal(t, perKey, EffectiveBuilderConfig(perKey, nil)) + }) + t.Run("min_bid inherits when per-key omits it", func(t *testing.T) { + def := &BuilderConfig{Enabled: proto.Bool(true), Builders: []*BuilderEntry{entryA, entryB}, MinBid: uint64ValPtr(5000000)} + perKey := &BuilderConfig{Enabled: proto.Bool(true), Builders: []*BuilderEntry{entryC}} + eff := EffectiveBuilderConfig(perKey, def) + require.NotNil(t, eff.MinBid) + require.Equal(t, validator.Uint64(5000000), *eff.MinBid) + require.Equal(t, 1, len(eff.Builders)) + require.Equal(t, "https://c", eff.Builders[0].URL) + }) + t.Run("explicit zero max payment is preserved, not inherited over", func(t *testing.T) { + def := &BuilderConfig{MaxExecutionPayment: uint64ValPtr(1000000000)} + perKey := &BuilderConfig{Enabled: proto.Bool(true), MaxExecutionPayment: uint64ValPtr(0)} + eff := EffectiveBuilderConfig(perKey, def) + require.NotNil(t, eff.MaxExecutionPayment) + require.Equal(t, validator.Uint64(0), *eff.MaxExecutionPayment) + }) + t.Run("unset max payment inherits default", func(t *testing.T) { + def := &BuilderConfig{MaxExecutionPayment: uint64ValPtr(1000000000)} + perKey := &BuilderConfig{Enabled: proto.Bool(true)} + eff := EffectiveBuilderConfig(perKey, def) + require.NotNil(t, eff.MaxExecutionPayment) + require.Equal(t, validator.Uint64(1000000000), *eff.MaxExecutionPayment) + }) + t.Run("explicit per-key disable wins over enabled default", func(t *testing.T) { + def := &BuilderConfig{Enabled: proto.Bool(true)} + perKey := &BuilderConfig{Enabled: proto.Bool(false), MinBid: uint64ValPtr(1)} + require.Equal(t, false, EffectiveBuilderConfig(perKey, def).IsEnabled()) + }) + t.Run("unset per-key enabled inherits enabled default", func(t *testing.T) { + def := &BuilderConfig{Enabled: proto.Bool(true)} + perKey := &BuilderConfig{MinBid: uint64ValPtr(1)} + require.Equal(t, true, EffectiveBuilderConfig(perKey, def).IsEnabled()) + }) + t.Run("present builders list replaces, never unions", func(t *testing.T) { + def := &BuilderConfig{Builders: []*BuilderEntry{entryA, entryB}} + perKey := &BuilderConfig{Builders: []*BuilderEntry{entryC}} + eff := EffectiveBuilderConfig(perKey, def) + require.Equal(t, 1, len(eff.Builders)) + require.Equal(t, "https://c", eff.Builders[0].URL) + }) + t.Run("absent builders list inherits default list", func(t *testing.T) { + def := &BuilderConfig{Builders: []*BuilderEntry{entryA, entryB}} + perKey := &BuilderConfig{Enabled: proto.Bool(true)} + require.Equal(t, 2, len(EffectiveBuilderConfig(perKey, def).Builders)) + }) + t.Run("zero gas limit inherits default gas limit", func(t *testing.T) { + def := &BuilderConfig{GasLimit: validator.Uint64(30000000)} + perKey := &BuilderConfig{Enabled: proto.Bool(true)} + require.Equal(t, validator.Uint64(30000000), EffectiveBuilderConfig(perKey, def).GasLimit) + }) + t.Run("proxy and boost inherit per field", func(t *testing.T) { + def := &BuilderConfig{Proxy: proto.String("http://side-car:9001"), BuilderBoostFactor: uint64ValPtr(90)} + perKey := &BuilderConfig{Enabled: proto.Bool(true), BuilderBoostFactor: uint64ValPtr(120)} + eff := EffectiveBuilderConfig(perKey, def) + require.Equal(t, "http://side-car:9001", *eff.Proxy) + require.Equal(t, validator.Uint64(120), *eff.BuilderBoostFactor) + }) + t.Run("both-set min_bid: per-key wins", func(t *testing.T) { + def := &BuilderConfig{MinBid: uint64ValPtr(5000000)} + perKey := &BuilderConfig{MinBid: uint64ValPtr(7000000)} + require.Equal(t, validator.Uint64(7000000), *EffectiveBuilderConfig(perKey, def).MinBid) + }) + t.Run("both-set proxy: per-key wins", func(t *testing.T) { + def := &BuilderConfig{Proxy: proto.String("http://default:1")} + perKey := &BuilderConfig{Proxy: proto.String("http://mine:2")} + require.Equal(t, "http://mine:2", *EffectiveBuilderConfig(perKey, def).Proxy) + }) + t.Run("nonzero per-key gas limit wins over default", func(t *testing.T) { + def := &BuilderConfig{GasLimit: validator.Uint64(30000000)} + perKey := &BuilderConfig{GasLimit: validator.Uint64(45000000)} + require.Equal(t, validator.Uint64(45000000), EffectiveBuilderConfig(perKey, def).GasLimit) + }) + t.Run("present relays replace default relays", func(t *testing.T) { + def := &BuilderConfig{Relays: []string{"https://r-def"}} + perKey := &BuilderConfig{Relays: []string{"https://r-key"}} + eff := EffectiveBuilderConfig(perKey, def) + require.Equal(t, 1, len(eff.Relays)) + require.Equal(t, "https://r-key", eff.Relays[0]) + }) + t.Run("absent relays inherit default relays", func(t *testing.T) { + def := &BuilderConfig{Relays: []string{"https://r-def"}} + perKey := &BuilderConfig{Enabled: proto.Bool(true)} + require.Equal(t, 1, len(EffectiveBuilderConfig(perKey, def).Relays)) + }) + // Builders and relays are independent fields: setting one per-key does not + // suppress inheriting the other. Documented composite, pinned deliberately. + t.Run("per-key relays plus default builders compose", func(t *testing.T) { + def := &BuilderConfig{Builders: []*BuilderEntry{entryA}} + perKey := &BuilderConfig{Relays: []string{"https://r-key"}} + eff := EffectiveBuilderConfig(perKey, def) + require.Equal(t, 1, len(eff.Builders)) + require.Equal(t, 1, len(eff.Relays)) + }) + t.Run("nil nil is nil", func(t *testing.T) { + require.Equal(t, (*BuilderConfig)(nil), EffectiveBuilderConfig(nil, nil)) + }) +} diff --git a/config/proposer/settings.go b/config/proposer/settings.go index 9a49ed161020..da71ef6eb4d6 100644 --- a/config/proposer/settings.go +++ b/config/proposer/settings.go @@ -63,9 +63,35 @@ func SettingFromConsensus(ps *validatorpb.ProposerSettingsPayload) (*Settings, e d.GasLimit = ps.DefaultConfig.GasLimit settings.DefaultConfig = d } + settings.normalizeLegacyPresence() return settings, nil } +// v1 payloads predate presence tracking: wire-absent enabled meant disabled, and +// the filesystem backend persisted a spurious explicit 0 max execution payment. +func (ps *Settings) normalizeLegacyPresence() { + if ps == nil || ps.isV2() { + return + } + normalize := func(opt *Option) { + if opt == nil || opt.BuilderConfig == nil { + return + } + bc := opt.BuilderConfig + if bc.Enabled == nil { + disabled := false + bc.Enabled = &disabled + } + if bc.MaxExecutionPayment != nil && *bc.MaxExecutionPayment == 0 { + bc.MaxExecutionPayment = nil + } + } + normalize(ps.DefaultConfig) + for _, opt := range ps.ProposeConfig { + normalize(opt) + } +} + func verifyOption(key string, option *validatorpb.ProposerOptionPayload) error { if option == nil { return fmt.Errorf("fee recipient is required for proposer %s", key) @@ -85,7 +111,7 @@ type BuilderConfig struct { Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` GasLimit validator.Uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"` Relays []string `json:"relays,omitempty" yaml:"relays,omitempty"` - MaxExecutionPayment validator.Uint64 `json:"max_execution_payment,omitempty" yaml:"max_execution_payment,omitempty"` + MaxExecutionPayment *validator.Uint64 `json:"max_execution_payment,omitempty" yaml:"max_execution_payment,omitempty"` // explicit 0 = trustless-only; unset inherits Builders []*BuilderEntry `json:"builders,omitempty" yaml:"builders,omitempty"` Proxy *string `json:"proxy,omitempty" yaml:"proxy,omitempty"` MinBid *validator.Uint64 `json:"min_bid,omitempty" yaml:"min_bid,omitempty"` @@ -110,18 +136,71 @@ func (bc *BuilderConfig) IsEnabled() bool { return bc != nil && bc.Enabled != nil && *bc.Enabled } +// EffectiveBuilderConfig resolves a key's builder config with field-level +// inheritance from the default: fields unset on perKey inherit def's values, and +// the builders/relays lists replace (never merge) when present on perKey. +func EffectiveBuilderConfig(perKey, def *BuilderConfig) *BuilderConfig { + if perKey == nil { + return def + } + if def == nil { + return perKey + } + eff := &BuilderConfig{ + Enabled: coalesceBool(perKey.Enabled, def.Enabled), + GasLimit: perKey.GasLimit, + MaxExecutionPayment: coalesceUint64(perKey.MaxExecutionPayment, def.MaxExecutionPayment), + MinBid: coalesceUint64(perKey.MinBid, def.MinBid), + BuilderBoostFactor: coalesceUint64(perKey.BuilderBoostFactor, def.BuilderBoostFactor), + Proxy: coalesceString(perKey.Proxy, def.Proxy), + Builders: perKey.Builders, + Relays: perKey.Relays, + } + if eff.GasLimit == 0 { + eff.GasLimit = def.GasLimit + } + if len(eff.Builders) == 0 { + eff.Builders = def.Builders + } + if len(eff.Relays) == 0 { + eff.Relays = def.Relays + } + return eff +} + +func coalesceBool(a, b *bool) *bool { + if a != nil { + return a + } + return b +} + +func coalesceString(a, b *string) *string { + if a != nil { + return a + } + return b +} + +func coalesceUint64(a, b *validator.Uint64) *validator.Uint64 { + if a != nil { + return a + } + return b +} + // BuilderConfigFromConsensus converts protobuf to a builder config used in in-memory storage func BuilderConfigFromConsensus(from *validatorpb.BuilderConfig) *BuilderConfig { if from == nil { return nil } c := &BuilderConfig{ - Enabled: from.Enabled, + Enabled: cloneBool(from.Enabled), GasLimit: from.GasLimit, - MaxExecutionPayment: from.MaxExecutionPayment, - Proxy: from.Proxy, - MinBid: from.MinBid, - BuilderBoostFactor: from.BuilderBoostFactor, + MaxExecutionPayment: cloneUint64(from.MaxExecutionPayment), + Proxy: cloneString(from.Proxy), + MinBid: cloneUint64(from.MinBid), + BuilderBoostFactor: cloneUint64(from.BuilderBoostFactor), } if from.Relays != nil { relays := make([]string, len(from.Relays)) @@ -292,7 +371,7 @@ func (bc *BuilderConfig) Clone() *BuilderConfig { c := &BuilderConfig{} c.Enabled = cloneBool(bc.Enabled) c.GasLimit = bc.GasLimit - c.MaxExecutionPayment = bc.MaxExecutionPayment + c.MaxExecutionPayment = cloneUint64(bc.MaxExecutionPayment) c.Proxy = cloneString(bc.Proxy) c.MinBid = cloneUint64(bc.MinBid) c.BuilderBoostFactor = cloneUint64(bc.BuilderBoostFactor) @@ -371,7 +450,7 @@ func (bc *BuilderConfig) ToConsensus() *validatorpb.BuilderConfig { c.Relays = relays } c.GasLimit = bc.GasLimit - c.MaxExecutionPayment = bc.MaxExecutionPayment + c.MaxExecutionPayment = cloneUint64(bc.MaxExecutionPayment) c.Proxy = cloneString(bc.Proxy) c.MinBid = cloneUint64(bc.MinBid) c.BuilderBoostFactor = cloneUint64(bc.BuilderBoostFactor) @@ -421,6 +500,7 @@ func (ps *Settings) UpgradeToV2() bool { if ps == nil || ps.isV2() { return false } + ps.normalizeLegacyPresence() migrate := func(opt *Option) { if opt == nil || opt.BuilderConfig == nil { return diff --git a/config/proposer/settings_test.go b/config/proposer/settings_test.go index bc6f71b65877..1ca27252a630 100644 --- a/config/proposer/settings_test.go +++ b/config/proposer/settings_test.go @@ -16,6 +16,11 @@ import ( "github.com/OffchainLabs/prysm/v7/testing/require" ) +func uint64ValPtr(v uint64) *validator.Uint64 { + u := validator.Uint64(v) + return &u +} + func Test_Proposer_Setting_Cloning(t *testing.T) { key1hex := "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a" key1, err := hexutil.Decode(key1hex) @@ -30,7 +35,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), Relays: []string{"https://example-relay.com"}, - MaxExecutionPayment: validator.Uint64(1000000000), + MaxExecutionPayment: uint64ValPtr(1000000000), }, }, }, @@ -42,7 +47,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { Enabled: proto.Bool(false), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), Relays: []string{"https://example-relay.com"}, - MaxExecutionPayment: validator.Uint64(2000000000), + MaxExecutionPayment: uint64ValPtr(2000000000), }, }, } @@ -71,7 +76,7 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { require.DeepEqual(t, config.Relays, clone.Relays) require.DeepEqual(t, config.Enabled, clone.Enabled) require.Equal(t, config.GasLimit, clone.GasLimit) - require.Equal(t, config.MaxExecutionPayment, clone.MaxExecutionPayment) + require.DeepEqual(t, config.MaxExecutionPayment, clone.MaxExecutionPayment) }) t.Run("To Payload and SettingFromConsensus", func(t *testing.T) { payload := settings.ToConsensus() @@ -616,3 +621,66 @@ func TestSettings_TargetGasLimit(t *testing.T) { require.Equal(t, chainDefault, ps.TargetGasLimit(pk)) }) } + +// Legacy (pre-v2) payloads can't express presence: wire-absent enabled meant +// disabled, and the filesystem backend wrote spurious explicit-0 max payments. +func TestSettingFromConsensus_NormalizesLegacyPresence(t *testing.T) { + key := [fieldparams.BLSPubkeyLength]byte{9} + legacy := &Settings{ + Version: SchemaV1, + DefaultConfig: &Option{BuilderConfig: &BuilderConfig{ + GasLimit: validator.Uint64(30000000), + MaxExecutionPayment: uint64ValPtr(0), + }}, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*Option{ + key: {BuilderConfig: &BuilderConfig{GasLimit: validator.Uint64(25000000)}}, + }, + } + + got, err := SettingFromConsensus(legacy.ToConsensus()) + require.NoError(t, err) + + def := got.DefaultConfig.BuilderConfig + require.NotNil(t, def.Enabled) + require.Equal(t, false, *def.Enabled) + require.Equal(t, (*validator.Uint64)(nil), def.MaxExecutionPayment) + + perKey := got.ProposeConfig[key].BuilderConfig + require.NotNil(t, perKey.Enabled) + require.Equal(t, false, *perKey.Enabled) + + // The normalized explicit false must survive the coalesce: a legacy disabled + // per-key section can never silently activate under an enabled default. + enabled := true + eff := EffectiveBuilderConfig(perKey, &BuilderConfig{Enabled: &enabled}) + require.Equal(t, false, eff.IsEnabled()) +} + +func TestSettingFromConsensus_V2PresencePreserved(t *testing.T) { + v2 := &Settings{ + Version: SchemaV2, + DefaultConfig: &Option{BuilderConfig: &BuilderConfig{ + MaxExecutionPayment: uint64ValPtr(0), + }}, + } + got, err := SettingFromConsensus(v2.ToConsensus()) + require.NoError(t, err) + bc := got.DefaultConfig.BuilderConfig + require.Equal(t, (*bool)(nil), bc.Enabled) + require.NotNil(t, bc.MaxExecutionPayment) + require.Equal(t, validator.Uint64(0), *bc.MaxExecutionPayment) +} + +func TestUpgradeToV2_NormalizesLegacyPresence(t *testing.T) { + ps := &Settings{ + Version: SchemaV1, + DefaultConfig: &Option{BuilderConfig: &BuilderConfig{MaxExecutionPayment: uint64ValPtr(0)}}, + } + require.Equal(t, true, ps.UpgradeToV2()) + bc := ps.DefaultConfig.BuilderConfig + require.NotNil(t, bc.Enabled) + require.Equal(t, false, *bc.Enabled) + require.Equal(t, (*validator.Uint64)(nil), bc.MaxExecutionPayment) + require.Equal(t, SchemaV2, ps.Version) + require.Equal(t, false, ps.UpgradeToV2()) +} diff --git a/proto/prysm/v1alpha1/validator-client/keymanager.pb.go b/proto/prysm/v1alpha1/validator-client/keymanager.pb.go index 4d4aedcde71c..c81e88e0863e 100755 --- a/proto/prysm/v1alpha1/validator-client/keymanager.pb.go +++ b/proto/prysm/v1alpha1/validator-client/keymanager.pb.go @@ -716,7 +716,7 @@ type BuilderConfig struct { Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` GasLimit github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` Relays []string `protobuf:"bytes,3,rep,name=relays,proto3" json:"relays,omitempty"` - MaxExecutionPayment github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,4,opt,name=max_execution_payment,json=maxExecutionPayment,proto3" json:"max_execution_payment,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` + MaxExecutionPayment *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,4,opt,name=max_execution_payment,json=maxExecutionPayment,proto3,oneof" json:"max_execution_payment,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` Builders []*BuilderEntry `protobuf:"bytes,5,rep,name=builders,proto3" json:"builders,omitempty"` Proxy *string `protobuf:"bytes,6,opt,name=proxy,proto3,oneof" json:"proxy,omitempty"` MinBid *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,7,opt,name=min_bid,json=minBid,proto3,oneof" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` @@ -777,8 +777,8 @@ func (x *BuilderConfig) GetRelays() []string { } func (x *BuilderConfig) GetMaxExecutionPayment() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { - if x != nil { - return x.MaxExecutionPayment + if x != nil && x.MaxExecutionPayment != nil { + return *x.MaxExecutionPayment } return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) } @@ -1192,7 +1192,7 @@ var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDesc = []byte 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x67, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x22, 0xa8, 0x05, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x22, 0xc7, 0x05, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x62, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, @@ -1203,112 +1203,114 @@ var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDesc = []byte 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x73, 0x12, 0x79, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x61, 0x79, 0x73, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x48, - 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, - 0x32, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, - 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, - 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, - 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, - 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x02, 0x52, 0x06, 0x6d, - 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, - 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, - 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, - 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, - 0x65, 0x64, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, - 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, - 0x72, 0x22, 0xbf, 0x04, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x88, 0x01, - 0x01, 0x12, 0x20, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, - 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, - 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, - 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, - 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, - 0x88, 0x01, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x04, 0x52, 0x13, 0x6d, 0x61, 0x78, + 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x01, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, - 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, + 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, + 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, + 0x62, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, + 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, + 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, + 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, + 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, + 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, + 0x36, 0x34, 0x48, 0x04, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, + 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, + 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x22, 0xbf, 0x04, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x88, 0x01, 0x01, + 0x12, 0x20, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, 0x88, + 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x02, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, + 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, + 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, + 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, + 0x01, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x05, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, - 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x0c, 0x0a, 0x0a, - 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, - 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, - 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, - 0x74, 0x6f, 0x72, 0x22, 0x81, 0x03, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, - 0x74, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, - 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, - 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x78, 0x0a, - 0x13, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xcd, 0x01, 0x0a, 0x22, 0x6f, 0x72, 0x67, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x42, 0x0f, - 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, - 0x01, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, - 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, - 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x70, 0x62, 0xaa, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5c, 0x41, 0x63, 0x63, 0x6f, - 0x75, 0x6e, 0x74, 0x73, 0x5c, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x04, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x88, + 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, + 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x05, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, + 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, + 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, + 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x22, 0x81, 0x03, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x74, + 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, + 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x78, 0x0a, 0x13, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xcd, 0x01, 0x0a, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x42, 0x0f, 0x4b, + 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, + 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, + 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x70, 0x62, 0xaa, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, + 0x74, 0x73, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5c, 0x41, 0x63, 0x63, 0x6f, 0x75, + 0x6e, 0x74, 0x73, 0x5c, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/prysm/v1alpha1/validator-client/keymanager.proto b/proto/prysm/v1alpha1/validator-client/keymanager.proto index 1fd2450f560c..786882353ec0 100644 --- a/proto/prysm/v1alpha1/validator-client/keymanager.proto +++ b/proto/prysm/v1alpha1/validator-client/keymanager.proto @@ -136,9 +136,9 @@ message BuilderConfig { ]; repeated string relays = 3; // max_execution_payment is the maximum execution layer payment (in Gwei) the - // proposer will accept from a builder for the Gloas builder API. 0 means - // trustless-only; 2^64-1 means accept any amount. - uint64 max_execution_payment = 4 [ + // proposer will accept from a builder for the Gloas builder API. Explicit 0 + // means trustless-only; unset inherits; 2^64-1 means accept any amount. + optional uint64 max_execution_payment = 4 [ (ethereum.eth.ext.cast_type) = "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" ]; diff --git a/validator/client/request_auth_test.go b/validator/client/request_auth_test.go index df853c71b359..700a5124a7da 100644 --- a/validator/client/request_auth_test.go +++ b/validator/client/request_auth_test.go @@ -6,6 +6,7 @@ import ( fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + validatortypes "github.com/OffchainLabs/prysm/v7/consensus-types/validator" ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/require" "google.golang.org/protobuf/proto" @@ -105,6 +106,119 @@ func TestSignRequestAuthCached_RotationRoundTrip(t *testing.T) { require.Equal(t, 0, len(v.signedRequestAuths)) } +// Field-level inheritance vectors through target resolution (keymanager #87 discussion). +func TestBuilderTargetsForKey_FieldLevelInheritance(t *testing.T) { + pk := [fieldparams.BLSPubkeyLength]byte{4} + minBid := validatortypes.Uint64(5000000) + zeroPayment := validatortypes.Uint64(0) + defaultOption := &proposer.Option{ + BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + Builders: []*proposer.BuilderEntry{{URL: "https://a"}, {URL: "https://b"}}, + MinBid: &minBid, + }, + } + + t.Run("per-key list replaces, min_bid inherits", func(t *testing.T) { + v := validator{proposerSettings: &proposer.Settings{ + DefaultConfig: defaultOption, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + pk: {BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + Builders: []*proposer.BuilderEntry{{URL: "https://c"}}, + }}, + }, + }} + targets := v.builderTargetsForKey(pk) + require.Equal(t, 1, len(targets)) + require.Equal(t, "https://c", targets[0].url) + require.NotNil(t, targets[0].minBid) + require.Equal(t, uint64(5000000), *targets[0].minBid) + }) + t.Run("enabled-only per-key inherits the default builder list", func(t *testing.T) { + v := validator{proposerSettings: &proposer.Settings{ + DefaultConfig: defaultOption, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + pk: {BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true)}}, + }, + }} + require.Equal(t, 2, len(v.builderTargetsForKey(pk))) + }) + t.Run("explicit zero max payment stays trustless-only", func(t *testing.T) { + defWithCap := &proposer.Option{BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + Builders: []*proposer.BuilderEntry{{URL: "https://a"}}, + MaxExecutionPayment: uint64ValPtrClient(1000000000), + }} + v := validator{proposerSettings: &proposer.Settings{ + DefaultConfig: defWithCap, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + pk: {BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + MaxExecutionPayment: &zeroPayment, + }}, + }, + }} + targets := v.builderTargetsForKey(pk) + require.Equal(t, 1, len(targets)) + require.Equal(t, uint64(0), targets[0].maxPayment) + }) + // Pins the full resolution chain: entry field -> effective config -> default_config. + // Entry X overrides everything; entry Y inherits config-level values, which + // themselves came from default_config through the coalesce. + t.Run("entry overrides beat config level, unset entry fields inherit through the chain", func(t *testing.T) { + defBoost := validatortypes.Uint64(90) + defMax := validatortypes.Uint64(100) + entryMin := validatortypes.Uint64(7000000) + entryBoost := validatortypes.Uint64(120) + entryMax := validatortypes.Uint64(50) + v := validator{proposerSettings: &proposer.Settings{ + DefaultConfig: &proposer.Option{BuilderConfig: &proposer.BuilderConfig{ + MinBid: &minBid, // 5000000 + BuilderBoostFactor: &defBoost, + MaxExecutionPayment: &defMax, + }}, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + pk: {BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + Builders: []*proposer.BuilderEntry{ + {URL: "https://x", MinBid: &entryMin, BuilderBoostFactor: &entryBoost, MaxExecutionPayment: &entryMax}, + {URL: "https://y"}, + }, + }}, + }, + }} + targets := v.builderTargetsForKey(pk) + require.Equal(t, 2, len(targets)) + + x := targets[0] + require.Equal(t, "https://x", x.url) + require.Equal(t, uint64(7000000), *x.minBid) + require.Equal(t, uint64(120), *x.boostFactor) + require.Equal(t, uint64(50), x.maxPayment) + + y := targets[1] + require.Equal(t, "https://y", y.url) + require.Equal(t, uint64(5000000), *y.minBid) + require.Equal(t, uint64(90), *y.boostFactor) + require.Equal(t, uint64(100), y.maxPayment) + }) + t.Run("per-key disable wins over enabled default", func(t *testing.T) { + v := validator{proposerSettings: &proposer.Settings{ + DefaultConfig: defaultOption, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + pk: {BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(false)}}, + }, + }} + require.Equal(t, 0, len(v.builderTargetsForKey(pk))) + }) +} + +func uint64ValPtrClient(v uint64) *validatortypes.Uint64 { + u := validatortypes.Uint64(v) + return &u +} + func TestSignedRequestAuthFor_KeyIncludesAuthData(t *testing.T) { pk := [fieldparams.BLSPubkeyLength]byte{3} slot := primitives.Slot(4) diff --git a/validator/client/validator.go b/validator/client/validator.go index 802217c1add7..d5128c7ab723 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1127,8 +1127,10 @@ func (v *validator) buildProposerSettingsRequests( // upgradeProposerSettingsToV2 migrates v1 proposer settings to v2 and persists // them. Callers must gate this on gloas-active so the pre-gloas registration // path still sees BuilderConfig. +// Upgrades a clone then swaps, so concurrent readers never observe a +// half-migrated settings object. func (v *validator) upgradeProposerSettingsToV2(ctx context.Context) { - ps := v.ProposerSettings() + ps := v.ProposerSettings().Clone() if !ps.UpgradeToV2() { return } @@ -1359,21 +1361,23 @@ type builderTarget struct { boostFactor *uint64 } +// builderConfigForKey returns pk's effective builder config: per-key fields win, +// unset fields inherit from default_config (field-level inheritance). func (v *validator) builderConfigForKey(pk pubkey) *proposer.BuilderConfig { ps := v.ProposerSettings() if ps == nil { return nil } - var bc *proposer.BuilderConfig + var def, perKey *proposer.BuilderConfig if ps.DefaultConfig != nil { - bc = ps.DefaultConfig.BuilderConfig + def = ps.DefaultConfig.BuilderConfig } if ps.ProposeConfig != nil { - if c, ok := ps.ProposeConfig[pk]; ok && c != nil && c.BuilderConfig != nil { - bc = c.BuilderConfig + if c, ok := ps.ProposeConfig[pk]; ok && c != nil { + perKey = c.BuilderConfig } } - return bc + return proposer.EffectiveBuilderConfig(perKey, def) } // builderTargetsForKey resolves the enabled builder list for pk. Each Builders[] @@ -1383,7 +1387,11 @@ func (v *validator) builderTargetsForKey(pk pubkey) []builderTarget { if !bc.IsEnabled() { return nil } - fbMax := uint64(bc.MaxExecutionPayment) + // Unset max payment resolves to 0 (trustless-only), the safe client default. + var fbMax uint64 + if bc.MaxExecutionPayment != nil { + fbMax = uint64(*bc.MaxExecutionPayment) + } fbMin := uint64Ptr(bc.MinBid) fbBoost := uint64Ptr(bc.BuilderBoostFactor) @@ -1540,7 +1548,7 @@ func (v *validator) buildSignedRegReqs( } if v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig == nil && v.ProposerSettings().DefaultConfig.BuilderConfig != nil { - log.Warn("Builder is `enabled` in default config but will be ignored because no fee recipient was provided!") + log.Warn("Default builder config has no default fee recipient; only keys with their own fee recipient can register") } for i, k := range activePubkeys { @@ -1551,39 +1559,37 @@ func (v *validator) buildSignedRegReqs( } feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex) - gasLimit := params.BeaconConfig().DefaultBuilderGasLimit - enabled := false + hasFeeRecipient := false + var defBC, perKeyBC *proposer.BuilderConfig - if v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig != nil { + if v.ProposerSettings().DefaultConfig != nil { defaultConfig := v.ProposerSettings().DefaultConfig - feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient // Use cli defaultBuilderConfig for fee recipient. - defaultBuilderConfig := defaultConfig.BuilderConfig - - if defaultBuilderConfig.IsEnabled() { - gasLimit = uint64(defaultBuilderConfig.GasLimit) // Use cli config for gas limit. - enabled = true + defBC = defaultConfig.BuilderConfig + if defaultConfig.FeeRecipientConfig != nil { + feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient + hasFeeRecipient = true } } - if v.ProposerSettings().ProposeConfig != nil { - config, ok := v.ProposerSettings().ProposeConfig[k] - if ok && config != nil && config.FeeRecipientConfig != nil { - feeRecipient = config.FeeRecipientConfig.FeeRecipient // Use file config for fee recipient. - builderConfig := config.BuilderConfig - if builderConfig != nil { - if builderConfig.IsEnabled() { - gasLimit = uint64(builderConfig.GasLimit) // Use file config for gas limit. - enabled = true - } else { - enabled = false // Custom config can disable validator from register. - } + if config, ok := v.ProposerSettings().ProposeConfig[k]; ok && config != nil { + perKeyBC = config.BuilderConfig + if config.FeeRecipientConfig != nil { + feeRecipient = config.FeeRecipientConfig.FeeRecipient + hasFeeRecipient = true } } } - if !enabled { + // Field-level resolution: per-key builder fields inherit from the default; + // registration still requires a fee recipient configured at some level. + bc := proposer.EffectiveBuilderConfig(perKeyBC, defBC) + if !bc.IsEnabled() || !hasFeeRecipient { continue } + gasLimit := params.BeaconConfig().DefaultBuilderGasLimit + if bc.GasLimit != 0 { + gasLimit = uint64(bc.GasLimit) + } req := ðpb.ValidatorRegistrationV1{ FeeRecipient: feeRecipient[:], diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index f50d5ec87627..5d7758cc942d 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -3264,7 +3264,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { client := validatormock.NewMockValidatorClient(ctrl) signature := blsmock.NewMockSignature(ctrl) - signature.EXPECT().Marshal().Return([]byte{}) + signature.EXPECT().Marshal().Return([]byte{}).AnyTimes() v := validator{ signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{}, @@ -3332,11 +3332,16 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { } actual := v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false) - assert.Equal(t, 1, len(actual)) + assert.Equal(t, 2, len(actual)) assert.DeepEqual(t, feeRecipient1[:], actual[0].Message.FeeRecipient) assert.Equal(t, uint64(1111), actual[0].Message.GasLimit) assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey) + // Field-level inheritance: pubkey3's enabled builder registers with its own + // gas limit and the inherited default fee recipient. + assert.DeepEqual(t, defaultFeeRecipient[:], actual[1].Message.FeeRecipient) + assert.Equal(t, uint64(3333), actual[1].Message.GasLimit) + assert.DeepEqual(t, pubkey3[:], actual[1].Message.Pubkey) } func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { @@ -3438,7 +3443,8 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey) assert.DeepEqual(t, defaultFeeRecipient[:], actual[1].Message.FeeRecipient) - assert.Equal(t, uint64(9999), actual[1].Message.GasLimit) + // Field-level inheritance: pubkey3's explicit per-key gas limit wins over the default's. + assert.Equal(t, uint64(3333), actual[1].Message.GasLimit) assert.DeepEqual(t, pubkey3[:], actual[1].Message.Pubkey) t.Run("mid epoch only pushes newly added key", func(t *testing.T) { diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index 75c0bbbd5dbb..f24abbdc66c7 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -209,7 +209,7 @@ func builderConfigJSONFromConsensus(bc *proposer.BuilderConfig) *BuilderConfigJs out := &BuilderConfigJson{ Enabled: bc.Enabled, Proxy: bc.Proxy, - MaxExecutionPayment: uintStrPtr(bc.MaxExecutionPayment), + MaxExecutionPayment: optUintStrPtr(bc.MaxExecutionPayment), MinBid: optUintStrPtr(bc.MinBid), BuilderBoostFactor: optUintStrPtr(bc.BuilderBoostFactor), } @@ -273,7 +273,7 @@ func builderConfigFromJSON(in *BuilderConfigJson) (*proposer.BuilderConfig, erro if err != nil { return nil, err } - bc.MaxExecutionPayment = v + bc.MaxExecutionPayment = &v } if in.MinBid != nil { v, err := parseUint(*in.MinBid, "builder.min_bid") @@ -359,13 +359,6 @@ func formatUint(v validator.Uint64) string { func strPtr(s string) *string { return &s } -func uintStrPtr(v validator.Uint64) *string { - if v == 0 { - return nil - } - return strPtr(formatUint(v)) -} - func optUintStrPtr(v *validator.Uint64) *string { if v == nil { return nil diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index 886663a2daba..4cefda42c3d7 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -229,3 +229,34 @@ func TestServer_SetValidatorConfig_ValidatorServiceNil(t *testing.T) { w := postValidatorConfig(t, s, `{"configs":{}}`) assert.Equal(t, http.StatusServiceUnavailable, w.Code) } + +// Pins the presence semantics of max_execution_payment at the API layer: +// explicit "0" (trustless-only) round-trips; unset stays absent from GET. +func TestServer_SetGetValidatorConfig_MaxExecutionPaymentZeroVsAbsent(t *testing.T) { + srv, keys := setupConfigServer(t, 2) + pkZero := hexutil.Encode(keys[0][:]) + pkAbsent := hexutil.Encode(keys[1][:]) + + body := `{"configs":{` + + `"` + pkZero + `":{"builder":{"enabled":true,"builders":[{"url":"http://a.example"}],"max_execution_payment":"0"}},` + + `"` + pkAbsent + `":{"builder":{"enabled":true,"builders":[{"url":"http://b.example"}]}}` + + `}}` + w := postValidatorConfig(t, srv, body) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusSet, setResp.Data[pkZero].Status) + require.Equal(t, configStatusSet, setResp.Data[pkAbsent].Status) + + getResp := getValidatorConfig(t, srv, "") + zero := getResp.Data.Configs[pkZero] + require.NotNil(t, zero) + require.NotNil(t, zero.Builder) + require.NotNil(t, zero.Builder.MaxExecutionPayment) + require.Equal(t, "0", *zero.Builder.MaxExecutionPayment) + + absent := getResp.Data.Configs[pkAbsent] + require.NotNil(t, absent) + require.NotNil(t, absent.Builder) + assert.Equal(t, (*string)(nil), absent.Builder.MaxExecutionPayment) +} From 6388b98fff20d8642e9a53f6779da589f39952a5 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 20 Jul 2026 15:11:25 -0500 Subject: [PATCH 05/19] self review --- .../prysm/v1alpha1/validator/proposer_bid.go | 7 +- .../validator/proposer_bid_prefs_test.go | 11 +++ .../validator/proposer_gloas_prefs_test.go | 79 +++++++++++++++++++ proto/prysm/v1alpha1/gloas_builder_api.proto | 5 +- validator/client/validator.go | 6 ++ validator/rpc/handlers_keymanager.go | 6 ++ validator/rpc/handlers_validator_config.go | 9 +++ .../rpc/handlers_validator_config_test.go | 16 ++++ 8 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index c2360ce6ee6e..24af73ddc00a 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -164,12 +164,17 @@ func builderBeatsLocal(builderValue, localValue primitives.Gwei, boostFactor uin } // The proposer's total take, the execution payment counts only up to the proposer's max preference. +// Saturates instead of wrapping: a malicious bid must not rank below honest ones by overflowing. func effectiveBidValue(bid *ethpb.SignedExecutionPayloadBid, maxExecutionPayment uint64) primitives.Gwei { payment := bid.Message.ExecutionPayment if uint64(payment) > maxExecutionPayment { payment = primitives.Gwei(maxExecutionPayment) } - return bid.Message.Value + payment + sum := bid.Message.Value + payment + if sum < bid.Message.Value { + return primitives.Gwei(math.MaxUint64) + } + return sum } // builderBidQuery carries the proposal context builder bids are requested and validated against. diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go index fcb52c70bec6..59418c818afc 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go @@ -115,3 +115,14 @@ func TestBuilderBeatsLocal(t *testing.T) { }) } } + +// A bid whose value+payment would wrap uint64 must saturate, never rank below honest bids. +func TestEffectiveBidValue_Saturates(t *testing.T) { + const builderIdx = primitives.BuilderIndex(3) + overflow := newBid(primitives.Gwei(math.MaxUint64-5), 100, builderIdx) + require.Equal(t, primitives.Gwei(math.MaxUint64), effectiveBidValue(overflow, math.MaxUint64)) + + honest := newBid(50, 25, builderIdx) + require.Equal(t, primitives.Gwei(75), effectiveBidValue(honest, math.MaxUint64)) + require.Equal(t, primitives.Gwei(60), effectiveBidValue(honest, 10)) +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go new file mode 100644 index 000000000000..9c0780f7ece9 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go @@ -0,0 +1,79 @@ +package validator + +import ( + "testing" + + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/testing/require" +) + +func prefEntry(url string, maxPayment uint64, minBid *uint64, boost *uint64) *ethpb.BuilderPreferenceV1 { + p := ðpb.BuilderPreferenceV1{ + Url: url, + Request: ðpb.BuilderPreferencesRequestV1{ + Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(maxPayment)}, + Auth: ðpb.SignedRequestAuthV1{Message: ðpb.RequestAuthV1{Data: []byte(url)}}, + }, + } + if minBid != nil { + g := primitives.Gwei(*minBid) + p.MinBid = &g + } + p.BuilderBoostFactor = boost + return p +} + +func TestBuilderAuthsAndPrefs(t *testing.T) { + minBid := uint64(7) + boost := uint64(120) + + t.Run("empty input yields no auths and neutral prefs", func(t *testing.T) { + auths, bp := builderAuthsAndPrefs(nil) + require.Equal(t, 0, len(auths)) + require.Equal(t, uint64(0), bp.maxPayment) + require.Equal(t, uint64(0), bp.minBid) + require.Equal(t, uint64(neutralBuilderBoostFactor), bp.boostFactor) + }) + t.Run("malformed entries are skipped and never set the baseline", func(t *testing.T) { + badMin := uint64(999) + entries := []*ethpb.BuilderPreferenceV1{ + nil, + {Url: "https://no-request", MinBid: func() *primitives.Gwei { g := primitives.Gwei(badMin); return &g }()}, + {Url: "https://no-auth", Request: ðpb.BuilderPreferencesRequestV1{}}, + func() *ethpb.BuilderPreferenceV1 { p := prefEntry("", 5, &badMin, nil); return p }(), + prefEntry("https://good", 42, &minBid, &boost), + } + auths, bp := builderAuthsAndPrefs(entries) + require.Equal(t, 1, len(auths)) + require.NotNil(t, auths["https://good"]) + require.Equal(t, uint64(42), bp.maxPayment) + require.Equal(t, minBid, bp.minBid) + require.Equal(t, boost, bp.boostFactor) + }) + t.Run("first valid entry sets the baseline, later entries do not override", func(t *testing.T) { + otherMin := uint64(1) + otherBoost := uint64(999) + auths, bp := builderAuthsAndPrefs([]*ethpb.BuilderPreferenceV1{ + prefEntry("https://a", 100, &minBid, &boost), + prefEntry("https://b", 200, &otherMin, &otherBoost), + }) + require.Equal(t, 2, len(auths)) + require.Equal(t, uint64(100), bp.maxPayment) + require.Equal(t, minBid, bp.minBid) + require.Equal(t, boost, bp.boostFactor) + }) + t.Run("duplicate url keeps the first auth", func(t *testing.T) { + first := prefEntry("https://dup", 1, nil, nil) + second := prefEntry("https://dup", 2, nil, nil) + auths, _ := builderAuthsAndPrefs([]*ethpb.BuilderPreferenceV1{first, second}) + require.Equal(t, 1, len(auths)) + require.Equal(t, first.Request.Auth, auths["https://dup"]) + }) + t.Run("unset min_bid and boost fall to client defaults", func(t *testing.T) { + _, bp := builderAuthsAndPrefs([]*ethpb.BuilderPreferenceV1{prefEntry("https://a", 5, nil, nil)}) + require.Equal(t, uint64(5), bp.maxPayment) + require.Equal(t, uint64(0), bp.minBid) + require.Equal(t, uint64(neutralBuilderBoostFactor), bp.boostFactor) + }) +} diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index 7d6c66346db3..56dd4d73c7c3 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -15,9 +15,8 @@ option go_package = "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1;eth"; // RequestAuthV1 authenticates a proposer's bid request to a specific builder. message RequestAuthV1 { - // data carries the builder URL. ByteList[MAX_DATA_SIZE=4096] per the spec, the - // list limit feeds the hash tree root, so a different value breaks signature - // verification. + // Opaque auth data agreed with the builder out of band (builder-specs 5078eab); + // the ByteList[4096] limit feeds the hash tree root and thus the signature. bytes data = 1 [ (ethereum.eth.ext.ssz_max) = "4096" ]; uint64 slot = 2 [ (ethereum.eth.ext.cast_type) = diff --git a/validator/client/validator.go b/validator/client/validator.go index d5128c7ab723..73bb3413ed54 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1488,6 +1488,12 @@ func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Sl prefs = append(prefs, p) } if len(prefs) == 0 { + if len(targets) > 0 { + log.WithFields(logrus.Fields{ + "pubkey": fmt.Sprintf("%#x", bytesutil.Trunc(pk[:])), + "slot": slot, + }).Warn("Builder is enabled but no signed request auths are warmed for this slot; proposing without builder preferences") + } return nil } return prefs diff --git a/validator/rpc/handlers_keymanager.go b/validator/rpc/handlers_keymanager.go index d32d7208a965..3c989a73ba29 100644 --- a/validator/rpc/handlers_keymanager.go +++ b/validator/rpc/handlers_keymanager.go @@ -868,6 +868,10 @@ func (s *Server) SetGraffiti(w http.ResponseWriter, r *http.Request) { return } + // Graffiti mutates proposer settings via a read-modify-write in the + // validator client, so it must serialize with the other settings writers. + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() if err := s.validatorService.SetGraffiti(ctx, bytesutil.ToBytes48(pubkey), []byte(req.Graffiti)); err != nil { httputil.HandleError(w, err.Error(), http.StatusInternalServerError) return @@ -888,6 +892,8 @@ func (s *Server) DeleteGraffiti(w http.ResponseWriter, r *http.Request) { return } + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() if err := s.validatorService.DeleteGraffiti(ctx, bytesutil.ToBytes48(pubkey)); err != nil { httputil.HandleError(w, err.Error(), http.StatusNotFound) return diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index f24abbdc66c7..09298dda08e0 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -3,12 +3,14 @@ package rpc import ( "encoding/json" "net/http" + "net/url" "strconv" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/validator" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" + "github.com/OffchainLabs/prysm/v7/io/logs" "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" "github.com/OffchainLabs/prysm/v7/network/httputil" "github.com/ethereum/go-ethereum/common" @@ -303,6 +305,13 @@ func builderEntryFromJSON(in *BuilderEntryJson, i int) (*proposer.BuilderEntry, if in.Url == "" { return nil, errors.Errorf("builder.builders[%d].url is required", i) } + if u, err := url.Parse(in.Url); err != nil || u.Scheme == "" || u.Host == "" { + return nil, errors.Errorf("builder.builders[%d].url is not a valid URL", i) + } + if in.AuthData == nil { + log.WithField("builder", logs.MaskCredentialsLogging(in.Url)).Warn( + "Builder entry has no auth_data; builders requiring an out-of-band agreement will reject its requests") + } be := &proposer.BuilderEntry{URL: in.Url, Proxy: in.Proxy} if in.Pubkey != nil { pk, err := hexutil.Decode(*in.Pubkey) diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index 4cefda42c3d7..61ac8130ca1c 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -260,3 +260,19 @@ func TestServer_SetGetValidatorConfig_MaxExecutionPaymentZeroVsAbsent(t *testing require.NotNil(t, absent.Builder) assert.Equal(t, (*string)(nil), absent.Builder.MaxExecutionPayment) } + +func TestServer_SetValidatorConfig_InvalidBuilderURL(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + + w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"builder":{"enabled":true,"builders":[{"url":"not a url at all"}]}}}}`) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusError, setResp.Data[pk].Status) + assert.StringContains(t, "url is not a valid URL", setResp.Data[pk].Message) + + // Nothing persisted for the key. + getResp := getValidatorConfig(t, srv, "") + require.Equal(t, 0, len(getResp.Data.Configs)) +} From c5975aad3e7671aa6f638ed6ae1e9926a569b579 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 08:58:26 -0500 Subject: [PATCH 06/19] reverting builder preference removal --- api/client/builder/client_gloas.go | 4 +- beacon-chain/builder/service.go | 12 +- beacon-chain/builder/service_gloas_test.go | 9 +- beacon-chain/builder/testing/mock.go | 12 +- .../v1alpha1/validator/builder_preferences.go | 37 ++++ .../validator/builder_preferences_test.go | 49 +++++ .../james-prysm_validator-config-endpoint.md | 17 +- config/proposer/loader/loader.go | 91 +++++--- config/proposer/loader/loader_test.go | 38 +++- proto/prysm/v1alpha1/gloas_builder_api.pb.go | 155 ++++++++++---- proto/prysm/v1alpha1/gloas_builder_api.proto | 10 + proto/prysm/v1alpha1/validator.pb.go | 196 +++++++++++------- proto/prysm/v1alpha1/validator.proto | 11 + testing/mock/beacon_validator_client_mock.go | 20 ++ testing/mock/beacon_validator_server_mock.go | 15 ++ .../validator-mock/validator_client_mock.go | 15 ++ .../beacon-api/beacon_api_validator_client.go | 6 + .../client/grpc-api/grpc_validator_client.go | 3 + validator/client/iface/validator_client.go | 1 + validator/client/request_auth_test.go | 34 +++ validator/client/validator.go | 49 ++++- validator/rpc/handlers_validator_config.go | 32 +-- .../rpc/handlers_validator_config_test.go | 31 ++- 23 files changed, 626 insertions(+), 221 deletions(-) create mode 100644 beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go create mode 100644 beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go diff --git a/api/client/builder/client_gloas.go b/api/client/builder/client_gloas.go index ffbad71a97ae..2f91bb675b17 100644 --- a/api/client/builder/client_gloas.go +++ b/api/client/builder/client_gloas.go @@ -179,8 +179,8 @@ func jsonSignedBeaconBlock(sb interfaces.ReadOnlySignedBeaconBlock) ([]byte, err return json.Marshal(jsonBlock) } -// SubmitBuilderPreferences submits a proposer's per-builder preferences ahead of the bid request. -// If the builder rejects the SSZ request, it retries once using JSON. +// SubmitBuilderPreferences submits a proposer's per-builder preferences ahead of the bid request +// (builder-specs preferences channel; currently unwired). Falls back to JSON if SSZ is rejected. func (c *Client) SubmitBuilderPreferences(ctx context.Context, validatorPubkey [48]byte, req *ethpb.BuilderPreferencesRequestV1) error { if req == nil { return errors.Wrap(errMalformedRequest, "nil builder preferences request") diff --git a/beacon-chain/builder/service.go b/beacon-chain/builder/service.go index cf1cf5b83878..78e036c162d6 100644 --- a/beacon-chain/builder/service.go +++ b/beacon-chain/builder/service.go @@ -31,7 +31,7 @@ type BlockBuilder interface { GetHeader(ctx context.Context, slot primitives.Slot, parentHash [32]byte, pubKey [48]byte) (builder.SignedBid, error) GetExecutionPayloadBid(ctx context.Context, slot primitives.Slot, parentHash, parentRoot [32]byte, proposerPubkey [48]byte, authsByURL map[string]*ethpb.SignedRequestAuthV1) ([]PayloadBid, error) SubmitSignedBeaconBlock(ctx context.Context, builderURL string, block interfaces.ReadOnlySignedBeaconBlock) error - SubmitBuilderPreferences(ctx context.Context, validatorPubkey [48]byte, req *ethpb.BuilderPreferencesRequestV1) error + SubmitBuilderPreferences(ctx context.Context, builderURL string, validatorPubkey [48]byte, req *ethpb.BuilderPreferencesRequestV1) error RegisterValidator(ctx context.Context, reg []*ethpb.SignedValidatorRegistrationV1) error RegistrationByValidatorID(ctx context.Context, id primitives.ValidatorIndex) (*ethpb.ValidatorRegistrationV1, error) Configured() bool @@ -226,15 +226,15 @@ func (s *Service) SubmitSignedBeaconBlock(ctx context.Context, builderURL string return c.SubmitSignedBeaconBlock(ctx, b) } -// Routed to the builder named in the signed request auth. -func (s *Service) SubmitBuilderPreferences(ctx context.Context, validatorPubkey [48]byte, req *ethpb.BuilderPreferencesRequestV1) error { +// SubmitBuilderPreferences forwards a proposer's signed preferences to the builder +// at builderURL (builder-specs preferences channel), ahead of the proposal slot. +func (s *Service) SubmitBuilderPreferences(ctx context.Context, builderURL string, validatorPubkey [48]byte, req *ethpb.BuilderPreferencesRequestV1) error { ctx, span := trace.StartSpan(ctx, "builder.SubmitBuilderPreferences") defer span.End() - url := string(req.GetAuth().GetMessage().GetData()) - if url == "" { + if builderURL == "" { return errors.New("builder preferences missing builder url") } - c, err := s.clientFor(url) + c, err := s.clientFor(builderURL) if err != nil { tracing.AnnotateError(span, err) return err diff --git a/beacon-chain/builder/service_gloas_test.go b/beacon-chain/builder/service_gloas_test.go index 989e27242416..002394932e5b 100644 --- a/beacon-chain/builder/service_gloas_test.go +++ b/beacon-chain/builder/service_gloas_test.go @@ -128,17 +128,18 @@ func TestClientFor_SeedsFlagClientAndCachesDials(t *testing.T) { require.Equal(t, 1, dialed) } -func TestSubmitBuilderPreferences_DialsPerURL(t *testing.T) { +func TestSubmitBuilderPreferences_DialsExplicitURL(t *testing.T) { fc := &fakeBuilderClient{url: "http://b"} s := newMultiplexService(t, map[string]*fakeBuilderClient{"http://b": fc}) req := ð.BuilderPreferencesRequestV1{ Preferences: ð.BuilderPreferencesV1{}, - Auth: authFor("http://b"), + Auth: authFor("opaque-auth-data"), } - require.NoError(t, s.SubmitBuilderPreferences(t.Context(), [48]byte{}, req)) + require.NoError(t, s.SubmitBuilderPreferences(t.Context(), "http://b", [48]byte{}, req)) require.Equal(t, 1, fc.prefCount) - err := s.SubmitBuilderPreferences(t.Context(), [48]byte{}, ð.BuilderPreferencesRequestV1{Auth: authFor("")}) + // The URL comes from the explicit parameter, never from the opaque auth data. + err := s.SubmitBuilderPreferences(t.Context(), "", [48]byte{}, req) require.ErrorContains(t, "missing builder url", err) } diff --git a/beacon-chain/builder/testing/mock.go b/beacon-chain/builder/testing/mock.go index 4320dce25b43..1ec58901d06b 100644 --- a/beacon-chain/builder/testing/mock.go +++ b/beacon-chain/builder/testing/mock.go @@ -40,11 +40,11 @@ type MockBuilderService struct { RegistrationCache *cache.RegistrationCache ErrGetHeader error ErrRegisterValidator error + ErrSubmitBuilderPreferences error PayloadBid *ethpb.SignedExecutionPayloadBid PayloadBids []beaconbuilder.PayloadBid ErrGetExecutionPayloadBid error ErrSubmitSignedBeaconBlock error - ErrSubmitBuilderPreferences error Cfg *Config } @@ -123,11 +123,6 @@ func (s *MockBuilderService) RegisterValidator(context.Context, []*ethpb.SignedV return s.ErrRegisterValidator } -// SubmitBuilderPreferences for mocking. -func (s *MockBuilderService) SubmitBuilderPreferences(_ context.Context, _ [48]byte, _ *ethpb.BuilderPreferencesRequestV1) error { - return s.ErrSubmitBuilderPreferences -} - // SubmitBlindedBlockPostFulu for mocking. func (s *MockBuilderService) SubmitBlindedBlockPostFulu(_ context.Context, _ interfaces.ReadOnlySignedBeaconBlock) error { return s.ErrSubmitBlindedBlockPostFulu @@ -148,3 +143,8 @@ func (s *MockBuilderService) GetExecutionPayloadBid(_ context.Context, _ primiti func (s *MockBuilderService) SubmitSignedBeaconBlock(_ context.Context, _ string, _ interfaces.ReadOnlySignedBeaconBlock) error { return s.ErrSubmitSignedBeaconBlock } + +// SubmitBuilderPreferences for mocking. +func (s *MockBuilderService) SubmitBuilderPreferences(_ context.Context, _ string, _ [48]byte, _ *ethpb.BuilderPreferencesRequestV1) error { + return s.ErrSubmitBuilderPreferences +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go new file mode 100644 index 000000000000..7810aa3c607c --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go @@ -0,0 +1,37 @@ +package validator + +import ( + "context" + + "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" + "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" +) + +// SubmitBuilderPreferences forwards a proposer's signed builder preferences to the +// named builder ahead of a proposal slot (builder-specs preferences channel). The +// beacon node is a pure conduit: bid-selection preferences travel inline with the +// block request instead. +func (vs *Server) SubmitBuilderPreferences(ctx context.Context, req *ethpb.SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { + ctx, span := trace.StartSpan(ctx, "ValidatorServer.SubmitBuilderPreferences") + defer span.End() + + if req == nil || req.Request == nil { + return nil, status.Error(codes.InvalidArgument, "builder preferences request is empty") + } + if req.Url == "" { + return nil, status.Error(codes.InvalidArgument, "builder preferences request is missing the builder url") + } + // Not gated on Configured(): gloas builders are dialed per URL rather than the endpoint flag. + if vs.BlockBuilder == nil { + return nil, status.Error(codes.FailedPrecondition, "builder is not configured") + } + pubkey := bytesutil.ToBytes48(req.ValidatorPubkey) + if err := vs.BlockBuilder.SubmitBuilderPreferences(ctx, req.Url, pubkey, req.Request); err != nil { + return nil, status.Errorf(codes.Internal, "could not submit builder preferences: %v", err) + } + return &emptypb.Empty{}, nil +} diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go new file mode 100644 index 000000000000..83669d7ddfb3 --- /dev/null +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go @@ -0,0 +1,49 @@ +package validator + +import ( + "testing" + + builderTest "github.com/OffchainLabs/prysm/v7/beacon-chain/builder/testing" + ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/testing/require" + "github.com/pkg/errors" +) + +func aotPrefReq(url string) *ethpb.SubmitBuilderPreferencesRequest { + return ðpb.SubmitBuilderPreferencesRequest{ + ValidatorPubkey: make([]byte, 48), + Url: url, + Request: ðpb.BuilderPreferencesRequestV1{ + Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: 1000}, + Auth: ðpb.SignedRequestAuthV1{Message: ðpb.RequestAuthV1{Data: []byte("opaque")}}, + }, + } +} + +func TestSubmitBuilderPreferences(t *testing.T) { + t.Run("forwards to the builder service", func(t *testing.T) { + vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} + _, err := vs.SubmitBuilderPreferences(t.Context(), aotPrefReq("http://b")) + require.NoError(t, err) + }) + t.Run("propagates builder errors", func(t *testing.T) { + vs := &Server{BlockBuilder: &builderTest.MockBuilderService{ErrSubmitBuilderPreferences: errors.New("boom")}} + _, err := vs.SubmitBuilderPreferences(t.Context(), aotPrefReq("http://b")) + require.ErrorContains(t, "could not submit builder preferences", err) + }) + t.Run("rejects missing url", func(t *testing.T) { + vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} + _, err := vs.SubmitBuilderPreferences(t.Context(), aotPrefReq("")) + require.ErrorContains(t, "missing the builder url", err) + }) + t.Run("rejects empty request", func(t *testing.T) { + vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} + _, err := vs.SubmitBuilderPreferences(t.Context(), ðpb.SubmitBuilderPreferencesRequest{Url: "http://b"}) + require.ErrorContains(t, "request is empty", err) + }) + t.Run("requires a builder", func(t *testing.T) { + vs := &Server{} + _, err := vs.SubmitBuilderPreferences(t.Context(), aotPrefReq("http://b")) + require.ErrorContains(t, "builder is not configured", err) + }) +} diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index 9d58f4a8d5d8..9e313b7df3dd 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -1,19 +1,20 @@ ### Added -- Add `GET`/`POST /eth/v1/validator/config` keymanager endpoints (keymanager-APIs #87), managing fee recipient, target gas limit, graffiti, and per-key builder preferences (builder list, `min_bid`, `max_execution_payment`, `builder_boost_factor`, proxy) as one atomic per-key document. POST replaces a key's fragment, an empty document clears it, and each key is applied independently with a `set`/`not_found`/`error` status. -- Wire per-key `builder_boost_factor` and `min_bid` into Gloas builder-API bid selection, and `builder_boost_factor` into the pre-Gloas `builder_boost_factor` block-production query. -- Accept `POST /eth/v4/validator/blocks/{slot}` carrying inline per-builder preferences (beacon-APIs #625 JIT model). +- Add `GET`/`POST /eth/v1/validator/config` keymanager endpoints for managing per-key proposer and builder preferences as a single document (keymanager-APIs #87). +- Add per-key `min_bid` and `builder_boost_factor` to Gloas bid selection, and per-key `builder_boost_factor` to pre-Gloas block requests. +- Accept `POST /eth/v4/validator/blocks/{slot}` with inline builder preferences (beacon-APIs #625). ### Changed -- Proposer builder config `enabled` and `max_execution_payment` are now presence-tracked: unset values inherit instead of defaulting (explicit `max_execution_payment: 0` still means trustless-only). -- Per-key builder config now resolves with field-level inheritance from `default_config`: unset fields inherit the default's values, and a present `builders`/`relays` list replaces the default's. Per-key builder settings no longer require a per-key fee recipient for validator registration (the default fee recipient is inherited). -- Builder preferences now travel inline with the block request (JIT) instead of being pushed ahead to a beacon-node cache; request auths sign the opaque per-builder `auth_data` (builder-specs #165) with the builder URL carried explicitly. +- Resolve per-key builder config with field-level inheritance from `default_config`; `enabled` and `max_execution_payment` are presence-tracked so unset values inherit. +- Per-key builder settings no longer require a per-key fee recipient to register; the default fee recipient is used. +- Send builder preferences inline with the block request instead of caching them on the beacon node; request auths sign opaque `auth_data` per builder-specs #165, with the builder URL carried separately. +- Merge file- or URL-loaded proposer settings per key: the file only overrides keys it names, and the settings schema version never regresses. ### Fixed -- Normalize legacy (pre-v2) proposer settings on load and at the v2 upgrade: wire-absent builder `enabled` becomes an explicit `false` (a previously disabled per-key builder section can never silently activate through inheritance), and the filesystem backend's spurious explicit-0 `max_execution_payment` is cleared so both database backends decode legacy data identically. +- Normalize legacy proposer settings on load so unset builder `enabled` stays disabled and both validator DB backends decode `max_execution_payment` identically. ### Removed -- Remove the `SubmitBuilderPreferences` internal gRPC RPC and the beacon-node builder-preference caches; `BlockRequest` field `builder_request_auths` is replaced by `builder_preferences`. +- Remove the beacon-node builder-preference caches; `BlockRequest.builder_request_auths` is replaced by `builder_preferences`. diff --git a/config/proposer/loader/loader.go b/config/proposer/loader/loader.go index b6604e0f9da7..ecf93e11e373 100644 --- a/config/proposer/loader/loader.go +++ b/config/proposer/loader/loader.go @@ -187,18 +187,6 @@ func (psl *SettingsLoader) Load(cliCtx *cli.Context) (*proposer.Settings, error) return ps, nil } -func hasBuilderShape(p *validatorpb.ProposerSettingsPayload) bool { - if p.DefaultConfig != nil && p.DefaultConfig.Builder != nil { - return true - } - for _, o := range p.ProposerConfig { - if o != nil && o.Builder != nil { - return true - } - } - return false -} - func (psl *SettingsLoader) applyOverrides() { if psl.options.builderConfig != nil && psl.options.gasLimit != nil { psl.options.builderConfig.GasLimit = *psl.options.gasLimit @@ -269,13 +257,22 @@ func (psl *SettingsLoader) processProposerSettings(loadedSettings, dbSettings *v // through Builder; v2 lives on Option directly. func mergeProposerSettings(loaded, db *validatorpb.ProposerSettingsPayload, options *flagOptions) *validatorpb.ProposerSettingsPayload { merged := &validatorpb.ProposerSettingsPayload{} + // The schema version never regresses: the highest version wins and the + // lower-version side's content is promoted before merging. if db != nil { merged.Version = db.Version } - // v1-shaped source content must not inherit DB's v2, else the runtime upgrade is skipped. - if loaded != nil && (loaded.Version != 0 || hasBuilderShape(loaded)) { + if loaded != nil && loaded.Version > merged.Version { merged.Version = loaded.Version } + if merged.Version == proposer.SchemaV2 { + if db != nil && db.Version < proposer.SchemaV2 { + promotePayloadToV2(db) + } + if loaded != nil && loaded.Version < proposer.SchemaV2 { + promotePayloadToV2(loaded) + } + } var builderConfig *validatorpb.BuilderConfig var gasLimitOnly *validator.Uint64 @@ -292,6 +289,52 @@ func mergeProposerSettings(loaded, db *validatorpb.ProposerSettingsPayload, opti return mergeProposerSettingsV1(merged, loaded, db, builderConfig, gasLimitOnly) } +// promotePayloadToV2 mirrors Settings.UpgradeToV2 plus legacy presence +// normalization at the payload level, so v1-shaped content merged into a v2 +// result is read correctly under v2 semantics. +func promotePayloadToV2(p *validatorpb.ProposerSettingsPayload) { + promote := func(opt *validatorpb.ProposerOptionPayload) { + if opt == nil || opt.Builder == nil { + return + } + if opt.GasLimit == 0 { + opt.GasLimit = opt.Builder.GasLimit + } + if opt.Builder.Enabled == nil { + disabled := false + opt.Builder.Enabled = &disabled + } + if opt.Builder.MaxExecutionPayment != nil && *opt.Builder.MaxExecutionPayment == 0 { + opt.Builder.MaxExecutionPayment = nil + } + } + promote(p.DefaultConfig) + for _, opt := range p.ProposerConfig { + promote(opt) + } +} + +// overlayProposerConfig merges per-key options: the loaded source wins only for +// keys it names; DB-resident keys it does not name are retained. +func overlayProposerConfig(db, loaded *validatorpb.ProposerSettingsPayload) map[string]*validatorpb.ProposerOptionPayload { + var out map[string]*validatorpb.ProposerOptionPayload + if db != nil && len(db.ProposerConfig) > 0 { + out = make(map[string]*validatorpb.ProposerOptionPayload, len(db.ProposerConfig)) + for k, v := range db.ProposerConfig { + out[k] = v + } + } + if loaded != nil && len(loaded.ProposerConfig) > 0 { + if out == nil { + out = make(map[string]*validatorpb.ProposerOptionPayload, len(loaded.ProposerConfig)) + } + for k, v := range loaded.ProposerConfig { + out[k] = v + } + } + return out +} + func mergeProposerSettingsV1(merged, loaded, db *validatorpb.ProposerSettingsPayload, builderConfig *validatorpb.BuilderConfig, gasLimitOnly *validator.Uint64) *validatorpb.ProposerSettingsPayload { stripDBBuilder := builderConfig == nil @@ -305,17 +348,12 @@ func mergeProposerSettingsV1(merged, loaded, db *validatorpb.ProposerSettingsPay merged.DefaultConfig = loaded.DefaultConfig } - if db != nil && len(db.ProposerConfig) > 0 { - merged.ProposerConfig = db.ProposerConfig - if stripDBBuilder { - for _, option := range db.ProposerConfig { - option.Builder = nil - } + if db != nil && stripDBBuilder { + for _, option := range db.ProposerConfig { + option.Builder = nil } } - if loaded != nil && len(loaded.ProposerConfig) > 0 { - merged.ProposerConfig = loaded.ProposerConfig - } + merged.ProposerConfig = overlayProposerConfig(db, loaded) if merged.DefaultConfig != nil { merged.DefaultConfig.Builder = processBuilderConfig(merged.DefaultConfig.Builder, builderConfig, gasLimitOnly) @@ -346,12 +384,7 @@ func mergeProposerSettingsV2(merged, loaded, db *validatorpb.ProposerSettingsPay if loaded != nil && loaded.DefaultConfig != nil { merged.DefaultConfig = loaded.DefaultConfig } - if db != nil && len(db.ProposerConfig) > 0 { - merged.ProposerConfig = db.ProposerConfig - } - if loaded != nil && len(loaded.ProposerConfig) > 0 { - merged.ProposerConfig = loaded.ProposerConfig - } + merged.ProposerConfig = overlayProposerConfig(db, loaded) if gasLimitOnly == nil { return merged diff --git a/config/proposer/loader/loader_test.go b/config/proposer/loader/loader_test.go index 773b8b96c67b..d4c041c36ed8 100644 --- a/config/proposer/loader/loader_test.go +++ b/config/proposer/loader/loader_test.go @@ -1182,7 +1182,7 @@ func Test_mergeProposerSettings_VersionPrecedence(t *testing.T) { ) require.Equal(t, uint32(proposer.SchemaV2), merged.Version) }) - t.Run("unversioned v1 content does not inherit v2 from db", func(t *testing.T) { + t.Run("v1 content merged into a v2 db is promoted, version never regresses", func(t *testing.T) { merged := mergeProposerSettings( &validatorpb.ProposerSettingsPayload{ DefaultConfig: &validatorpb.ProposerOptionPayload{ @@ -1192,13 +1192,36 @@ func Test_mergeProposerSettings_VersionPrecedence(t *testing.T) { &validatorpb.ProposerSettingsPayload{Version: proposer.SchemaV2}, &flagOptions{}, ) - require.Equal(t, uint32(0), merged.Version) + require.Equal(t, uint32(proposer.SchemaV2), merged.Version) require.NotNil(t, merged.DefaultConfig.Builder) + // The builder gas limit is promoted so v2 reads see it at the top level. + require.Equal(t, validator.Uint64(30000000), merged.DefaultConfig.GasLimit) + }) + t.Run("file wins only for keys it names", func(t *testing.T) { + dbPayload := &validatorpb.ProposerSettingsPayload{ + Version: proposer.SchemaV2, + ProposerConfig: map[string]*validatorpb.ProposerOptionPayload{ + "0xaa": {FeeRecipient: "0x1111111111111111111111111111111111111111"}, + "0xbb": {FeeRecipient: "0x2222222222222222222222222222222222222222", GasLimit: 45000000}, + }, + } + filePayload := &validatorpb.ProposerSettingsPayload{ + Version: proposer.SchemaV2, + ProposerConfig: map[string]*validatorpb.ProposerOptionPayload{ + "0xaa": {FeeRecipient: "0x3333333333333333333333333333333333333333"}, + }, + } + merged := mergeProposerSettings(filePayload, dbPayload, &flagOptions{}) + require.Equal(t, 2, len(merged.ProposerConfig)) + require.Equal(t, "0x3333333333333333333333333333333333333333", merged.ProposerConfig["0xaa"].FeeRecipient) + // The key the file does not name keeps its DB-resident settings. + require.Equal(t, "0x2222222222222222222222222222222222222222", merged.ProposerConfig["0xbb"].FeeRecipient) + require.Equal(t, validator.Uint64(45000000), merged.ProposerConfig["0xbb"].GasLimit) }) } -// Restarting with the same v1 file after migration persisted v2 to the DB must -// reload in v1 form so the runtime upgrade re-applies the file's gas limits. +// Restarting with the same v1 file after migration persisted v2 to the DB keeps +// the v2 version and promotes the file's content so its gas limits stay readable. func TestSettingsLoader_V1FileAfterMigratedDB(t *testing.T) { params.SetupTestConfigCleanup(t) cfg := params.BeaconConfig().Copy() @@ -1225,12 +1248,13 @@ func TestSettingsLoader_V1FileAfterMigratedDB(t *testing.T) { require.NoError(t, err) require.NotNil(t, got) - require.Equal(t, uint32(0), got.Version) + require.Equal(t, proposer.SchemaV2, got.Version) require.NotNil(t, got.DefaultConfig.BuilderConfig) require.Equal(t, validator.Uint64(40000000), got.DefaultConfig.BuilderConfig.GasLimit) - assert.LogsContain(t, hook, "deprecated v1 schema") + assert.LogsDoNotContain(t, hook, "deprecated v1 schema") - require.Equal(t, true, got.UpgradeToV2()) + // Already v2: the file's builder gas limits were promoted at merge time. + require.Equal(t, false, got.UpgradeToV2()) key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a") require.NoError(t, err) require.Equal(t, validator.Uint64(60000000), got.GasLimit(bytesutil.ToBytes48(key1))) diff --git a/proto/prysm/v1alpha1/gloas_builder_api.pb.go b/proto/prysm/v1alpha1/gloas_builder_api.pb.go index ef12acddceee..6225d9a15174 100755 --- a/proto/prysm/v1alpha1/gloas_builder_api.pb.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.pb.go @@ -223,6 +223,66 @@ func (x *BuilderPreferencesRequestV1) GetAuth() *SignedRequestAuthV1 { return nil } +type SubmitBuilderPreferencesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ValidatorPubkey []byte `protobuf:"bytes,1,opt,name=validator_pubkey,json=validatorPubkey,proto3" json:"validator_pubkey,omitempty"` + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` + Request *BuilderPreferencesRequestV1 `protobuf:"bytes,3,opt,name=request,proto3" json:"request,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitBuilderPreferencesRequest) Reset() { + *x = SubmitBuilderPreferencesRequest{} + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitBuilderPreferencesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitBuilderPreferencesRequest) ProtoMessage() {} + +func (x *SubmitBuilderPreferencesRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SubmitBuilderPreferencesRequest.ProtoReflect.Descriptor instead. +func (*SubmitBuilderPreferencesRequest) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP(), []int{4} +} + +func (x *SubmitBuilderPreferencesRequest) GetValidatorPubkey() []byte { + if x != nil { + return x.ValidatorPubkey + } + return nil +} + +func (x *SubmitBuilderPreferencesRequest) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *SubmitBuilderPreferencesRequest) GetRequest() *BuilderPreferencesRequestV1 { + if x != nil { + return x.Request + } + return nil +} + type BuilderPreferenceV1 struct { state protoimpl.MessageState `protogen:"open.v1"` Request *BuilderPreferencesRequestV1 `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` @@ -235,7 +295,7 @@ type BuilderPreferenceV1 struct { func (x *BuilderPreferenceV1) Reset() { *x = BuilderPreferenceV1{} - mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4] + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -247,7 +307,7 @@ func (x *BuilderPreferenceV1) String() string { func (*BuilderPreferenceV1) ProtoMessage() {} func (x *BuilderPreferenceV1) ProtoReflect() protoreflect.Message { - mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4] + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -260,7 +320,7 @@ func (x *BuilderPreferenceV1) ProtoReflect() protoreflect.Message { // Deprecated: Use BuilderPreferenceV1.ProtoReflect.Descriptor instead. func (*BuilderPreferenceV1) Descriptor() ([]byte, []int) { - return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP(), []int{4} + return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP(), []int{5} } func (x *BuilderPreferenceV1) GetRequest() *BuilderPreferencesRequestV1 { @@ -336,31 +396,42 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x75, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, - 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xb5, 0x02, 0x0a, 0x13, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x56, 0x31, 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x62, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, - 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, - 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, - 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, - 0x75, 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x42, 0x0a, - 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, - 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, - 0x74, 0x6f, 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x75, 0x74, 0x68, 0x56, 0x31, 0x52, 0x04, 0x61, 0x75, 0x74, 0x68, 0x22, 0xac, 0x01, 0x0a, 0x1f, + 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x29, 0x0a, 0x10, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x50, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x4c, 0x0a, 0x07, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, + 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb5, 0x02, 0x0a, 0x13, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, + 0x56, 0x31, 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x62, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, - 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, + 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x73, 0x2e, 0x47, 0x77, 0x65, 0x69, 0x48, 0x00, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x35, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, + 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x01, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, + 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x42, 0x0a, 0x0a, + 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, + 0x6f, 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -375,24 +446,26 @@ func file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP() []byte { return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescData } -var file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes = make([]protoimpl.MessageInfo, 6) var file_proto_prysm_v1alpha1_gloas_builder_api_proto_goTypes = []any{ - (*RequestAuthV1)(nil), // 0: ethereum.eth.v1alpha1.RequestAuthV1 - (*SignedRequestAuthV1)(nil), // 1: ethereum.eth.v1alpha1.SignedRequestAuthV1 - (*BuilderPreferencesV1)(nil), // 2: ethereum.eth.v1alpha1.BuilderPreferencesV1 - (*BuilderPreferencesRequestV1)(nil), // 3: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 - (*BuilderPreferenceV1)(nil), // 4: ethereum.eth.v1alpha1.BuilderPreferenceV1 + (*RequestAuthV1)(nil), // 0: ethereum.eth.v1alpha1.RequestAuthV1 + (*SignedRequestAuthV1)(nil), // 1: ethereum.eth.v1alpha1.SignedRequestAuthV1 + (*BuilderPreferencesV1)(nil), // 2: ethereum.eth.v1alpha1.BuilderPreferencesV1 + (*BuilderPreferencesRequestV1)(nil), // 3: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 + (*SubmitBuilderPreferencesRequest)(nil), // 4: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest + (*BuilderPreferenceV1)(nil), // 5: ethereum.eth.v1alpha1.BuilderPreferenceV1 } var file_proto_prysm_v1alpha1_gloas_builder_api_proto_depIdxs = []int32{ 0, // 0: ethereum.eth.v1alpha1.SignedRequestAuthV1.message:type_name -> ethereum.eth.v1alpha1.RequestAuthV1 2, // 1: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1.preferences:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesV1 1, // 2: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1.auth:type_name -> ethereum.eth.v1alpha1.SignedRequestAuthV1 - 3, // 3: ethereum.eth.v1alpha1.BuilderPreferenceV1.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 - 4, // [4:4] is the sub-list for method output_type - 4, // [4:4] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 3, // 3: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 + 3, // 4: ethereum.eth.v1alpha1.BuilderPreferenceV1.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name } func init() { file_proto_prysm_v1alpha1_gloas_builder_api_proto_init() } @@ -400,14 +473,14 @@ func file_proto_prysm_v1alpha1_gloas_builder_api_proto_init() { if File_proto_prysm_v1alpha1_gloas_builder_api_proto != nil { return } - file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[4].OneofWrappers = []any{} + file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[5].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 6, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index 56dd4d73c7c3..2d8cdc79fb14 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -42,6 +42,16 @@ message BuilderPreferencesRequestV1 { SignedRequestAuthV1 auth = 2; } +// SubmitBuilderPreferencesRequest asks the beacon node to forward a proposer's +// signed preferences to one builder ahead of a proposal slot (the builder-specs +// preferences channel). url identifies the builder explicitly: per builder-specs +// 5078eab the signed auth data is opaque and no longer carries the URL. +message SubmitBuilderPreferencesRequest { + bytes validator_pubkey = 1; + string url = 2; + BuilderPreferencesRequestV1 request = 3; +} + // BuilderPreferenceV1 is one builder's preferences for a single block proposal, // carried inline on the block request (JIT model, beacon-APIs #625). request is // the signed preferences plus auth the beacon node forwards to the builder; diff --git a/proto/prysm/v1alpha1/validator.pb.go b/proto/prysm/v1alpha1/validator.pb.go index e67df0e78228..ead733c56370 100755 --- a/proto/prysm/v1alpha1/validator.pb.go +++ b/proto/prysm/v1alpha1/validator.pb.go @@ -4942,7 +4942,7 @@ var file_proto_prysm_v1alpha1_validator_proto_rawDesc = []byte{ 0x53, 0x4c, 0x41, 0x53, 0x48, 0x49, 0x4e, 0x47, 0x10, 0x05, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x58, 0x49, 0x54, 0x45, 0x44, 0x10, 0x06, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56, 0x41, 0x4c, 0x49, 0x44, 0x10, 0x07, 0x12, 0x17, 0x0a, 0x13, 0x50, 0x41, 0x52, 0x54, 0x49, 0x41, 0x4c, 0x4c, 0x59, - 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x44, 0x10, 0x08, 0x32, 0xcb, 0x38, 0x0a, + 0x5f, 0x44, 0x45, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x45, 0x44, 0x10, 0x08, 0x32, 0xf3, 0x39, 0x0a, 0x13, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x4e, 0x6f, 0x64, 0x65, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x44, 0x75, 0x74, 0x69, 0x65, 0x73, 0x12, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, @@ -5385,27 +5385,37 @@ var file_proto_prysm_v1alpha1_validator_proto_rawDesc = []byte{ 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, - 0xa8, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x42, 0x69, 0x64, 0x12, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x42, 0x69, 0x64, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3b, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x3a, 0x01, 0x2a, 0x22, 0x2d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x88, 0x02, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x19, 0x6f, - 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, - 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0xa5, 0x01, 0x0a, 0x18, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x36, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x39, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x30, 0x3a, 0x01, 0x2a, 0x22, 0x2b, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x88, 0x02, 0x01, 0x12, 0xa8, 0x01, 0x0a, 0x1f, 0x53, 0x75, 0x62, 0x6d, + 0x69, 0x74, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x69, 0x64, 0x12, 0x30, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x42, 0x69, 0x64, 0x1a, 0x16, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x3b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x32, 0x3a, 0x01, 0x2a, + 0x22, 0x2d, 0x2f, 0x65, 0x74, 0x68, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, + 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x62, 0x69, 0x64, 0x88, + 0x02, 0x01, 0x42, 0x92, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x42, 0x0e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, + 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, + 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x0f, + 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x56, 0x31, 0xca, + 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -5516,11 +5526,12 @@ var file_proto_prysm_v1alpha1_validator_proto_goTypes = []any{ (*GenericSignedExecutionPayloadEnvelope)(nil), // 90: ethereum.eth.v1alpha1.GenericSignedExecutionPayloadEnvelope (*PayloadAttestationMessage)(nil), // 91: ethereum.eth.v1alpha1.PayloadAttestationMessage (*SubmitSignedProposerPreferencesRequest)(nil), // 92: ethereum.eth.v1alpha1.SubmitSignedProposerPreferencesRequest - (*SignedExecutionPayloadBid)(nil), // 93: ethereum.eth.v1alpha1.SignedExecutionPayloadBid - (*GenericBeaconBlock)(nil), // 94: ethereum.eth.v1alpha1.GenericBeaconBlock - (*AttestationData)(nil), // 95: ethereum.eth.v1alpha1.AttestationData - (*SyncCommitteeContribution)(nil), // 96: ethereum.eth.v1alpha1.SyncCommitteeContribution - (*PayloadAttestationData)(nil), // 97: ethereum.eth.v1alpha1.PayloadAttestationData + (*SubmitBuilderPreferencesRequest)(nil), // 93: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest + (*SignedExecutionPayloadBid)(nil), // 94: ethereum.eth.v1alpha1.SignedExecutionPayloadBid + (*GenericBeaconBlock)(nil), // 95: ethereum.eth.v1alpha1.GenericBeaconBlock + (*AttestationData)(nil), // 96: ethereum.eth.v1alpha1.AttestationData + (*SyncCommitteeContribution)(nil), // 97: ethereum.eth.v1alpha1.SyncCommitteeContribution + (*PayloadAttestationData)(nil), // 98: ethereum.eth.v1alpha1.PayloadAttestationData } var file_proto_prysm_v1alpha1_validator_proto_depIdxs = []int32{ 68, // 0: ethereum.eth.v1alpha1.StreamBlocksResponse.phase0_block:type_name -> ethereum.eth.v1alpha1.SignedBeaconBlock @@ -5597,51 +5608,53 @@ var file_proto_prysm_v1alpha1_validator_proto_depIdxs = []int32{ 61, // 71: ethereum.eth.v1alpha1.BeaconNodeValidator.PayloadAttestationData:input_type -> ethereum.eth.v1alpha1.PayloadAttestationDataRequest 91, // 72: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitPayloadAttestation:input_type -> ethereum.eth.v1alpha1.PayloadAttestationMessage 92, // 73: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedProposerPreferences:input_type -> ethereum.eth.v1alpha1.SubmitSignedProposerPreferencesRequest - 93, // 74: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:input_type -> ethereum.eth.v1alpha1.SignedExecutionPayloadBid - 20, // 75: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDuties:output_type -> ethereum.eth.v1alpha1.DutiesResponse - 21, // 76: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDutiesV2:output_type -> ethereum.eth.v1alpha1.DutiesV2Response - 48, // 77: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttesterDuties:output_type -> ethereum.eth.v1alpha1.AttesterDutiesResponse - 51, // 78: ethereum.eth.v1alpha1.BeaconNodeValidator.GetProposerDutiesV2:output_type -> ethereum.eth.v1alpha1.ProposerDutiesResponse - 54, // 79: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeDuties:output_type -> ethereum.eth.v1alpha1.SyncCommitteeDutiesResponse - 57, // 80: ethereum.eth.v1alpha1.BeaconNodeValidator.GetPTCDuties:output_type -> ethereum.eth.v1alpha1.PTCDutiesResponse - 8, // 81: ethereum.eth.v1alpha1.BeaconNodeValidator.DomainData:output_type -> ethereum.eth.v1alpha1.DomainResponse - 11, // 82: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForChainStart:output_type -> ethereum.eth.v1alpha1.ChainStartResponse - 10, // 83: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForActivation:output_type -> ethereum.eth.v1alpha1.ValidatorActivationResponse - 14, // 84: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorIndex:output_type -> ethereum.eth.v1alpha1.ValidatorIndexResponse - 16, // 85: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorStatus:output_type -> ethereum.eth.v1alpha1.ValidatorStatusResponse - 18, // 86: ethereum.eth.v1alpha1.BeaconNodeValidator.MultipleValidatorStatus:output_type -> ethereum.eth.v1alpha1.MultipleValidatorStatusResponse - 94, // 87: ethereum.eth.v1alpha1.BeaconNodeValidator.GetBeaconBlock:output_type -> ethereum.eth.v1alpha1.GenericBeaconBlock - 23, // 88: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeBeaconBlock:output_type -> ethereum.eth.v1alpha1.ProposeResponse - 83, // 89: ethereum.eth.v1alpha1.BeaconNodeValidator.PrepareBeaconProposer:output_type -> google.protobuf.Empty - 43, // 90: ethereum.eth.v1alpha1.BeaconNodeValidator.GetFeeRecipientByPubKey:output_type -> ethereum.eth.v1alpha1.FeeRecipientByPubKeyResponse - 95, // 91: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttestationData:output_type -> ethereum.eth.v1alpha1.AttestationData - 26, // 92: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestation:output_type -> ethereum.eth.v1alpha1.AttestResponse - 26, // 93: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestationElectra:output_type -> ethereum.eth.v1alpha1.AttestResponse - 28, // 94: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.AggregateSelectionResponse - 29, // 95: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.AggregateSelectionElectraResponse - 32, // 96: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse - 32, // 97: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse - 24, // 98: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeExit:output_type -> ethereum.eth.v1alpha1.ProposeExitResponse - 83, // 99: ethereum.eth.v1alpha1.BeaconNodeValidator.SubscribeCommitteeSubnets:output_type -> google.protobuf.Empty - 38, // 100: ethereum.eth.v1alpha1.BeaconNodeValidator.CheckDoppelGanger:output_type -> ethereum.eth.v1alpha1.DoppelGangerResponse - 1, // 101: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncMessageBlockRoot:output_type -> ethereum.eth.v1alpha1.SyncMessageBlockRootResponse - 83, // 102: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSyncMessage:output_type -> google.protobuf.Empty - 4, // 103: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncSubcommitteeIndex:output_type -> ethereum.eth.v1alpha1.SyncSubcommitteeIndexResponse - 96, // 104: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeContribution:output_type -> ethereum.eth.v1alpha1.SyncCommitteeContribution - 83, // 105: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedContributionAndProof:output_type -> google.protobuf.Empty - 5, // 106: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamSlots:output_type -> ethereum.eth.v1alpha1.StreamSlotsResponse - 6, // 107: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamBlocksAltair:output_type -> ethereum.eth.v1alpha1.StreamBlocksResponse - 83, // 108: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitValidatorRegistrations:output_type -> google.protobuf.Empty - 83, // 109: ethereum.eth.v1alpha1.BeaconNodeValidator.AssignValidatorToSubnet:output_type -> google.protobuf.Empty - 46, // 110: ethereum.eth.v1alpha1.BeaconNodeValidator.AggregatedSigAndAggregationBits:output_type -> ethereum.eth.v1alpha1.AggregatedSigAndAggregationBitsResponse - 60, // 111: ethereum.eth.v1alpha1.BeaconNodeValidator.GetExecutionPayloadEnvelope:output_type -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelopeResponse - 83, // 112: ethereum.eth.v1alpha1.BeaconNodeValidator.PublishExecutionPayloadEnvelope:output_type -> google.protobuf.Empty - 97, // 113: ethereum.eth.v1alpha1.BeaconNodeValidator.PayloadAttestationData:output_type -> ethereum.eth.v1alpha1.PayloadAttestationData - 83, // 114: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitPayloadAttestation:output_type -> google.protobuf.Empty - 83, // 115: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedProposerPreferences:output_type -> google.protobuf.Empty - 83, // 116: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:output_type -> google.protobuf.Empty - 75, // [75:117] is the sub-list for method output_type - 33, // [33:75] is the sub-list for method input_type + 93, // 74: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitBuilderPreferences:input_type -> ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest + 94, // 75: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:input_type -> ethereum.eth.v1alpha1.SignedExecutionPayloadBid + 20, // 76: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDuties:output_type -> ethereum.eth.v1alpha1.DutiesResponse + 21, // 77: ethereum.eth.v1alpha1.BeaconNodeValidator.GetDutiesV2:output_type -> ethereum.eth.v1alpha1.DutiesV2Response + 48, // 78: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttesterDuties:output_type -> ethereum.eth.v1alpha1.AttesterDutiesResponse + 51, // 79: ethereum.eth.v1alpha1.BeaconNodeValidator.GetProposerDutiesV2:output_type -> ethereum.eth.v1alpha1.ProposerDutiesResponse + 54, // 80: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeDuties:output_type -> ethereum.eth.v1alpha1.SyncCommitteeDutiesResponse + 57, // 81: ethereum.eth.v1alpha1.BeaconNodeValidator.GetPTCDuties:output_type -> ethereum.eth.v1alpha1.PTCDutiesResponse + 8, // 82: ethereum.eth.v1alpha1.BeaconNodeValidator.DomainData:output_type -> ethereum.eth.v1alpha1.DomainResponse + 11, // 83: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForChainStart:output_type -> ethereum.eth.v1alpha1.ChainStartResponse + 10, // 84: ethereum.eth.v1alpha1.BeaconNodeValidator.WaitForActivation:output_type -> ethereum.eth.v1alpha1.ValidatorActivationResponse + 14, // 85: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorIndex:output_type -> ethereum.eth.v1alpha1.ValidatorIndexResponse + 16, // 86: ethereum.eth.v1alpha1.BeaconNodeValidator.ValidatorStatus:output_type -> ethereum.eth.v1alpha1.ValidatorStatusResponse + 18, // 87: ethereum.eth.v1alpha1.BeaconNodeValidator.MultipleValidatorStatus:output_type -> ethereum.eth.v1alpha1.MultipleValidatorStatusResponse + 95, // 88: ethereum.eth.v1alpha1.BeaconNodeValidator.GetBeaconBlock:output_type -> ethereum.eth.v1alpha1.GenericBeaconBlock + 23, // 89: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeBeaconBlock:output_type -> ethereum.eth.v1alpha1.ProposeResponse + 83, // 90: ethereum.eth.v1alpha1.BeaconNodeValidator.PrepareBeaconProposer:output_type -> google.protobuf.Empty + 43, // 91: ethereum.eth.v1alpha1.BeaconNodeValidator.GetFeeRecipientByPubKey:output_type -> ethereum.eth.v1alpha1.FeeRecipientByPubKeyResponse + 96, // 92: ethereum.eth.v1alpha1.BeaconNodeValidator.GetAttestationData:output_type -> ethereum.eth.v1alpha1.AttestationData + 26, // 93: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestation:output_type -> ethereum.eth.v1alpha1.AttestResponse + 26, // 94: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeAttestationElectra:output_type -> ethereum.eth.v1alpha1.AttestResponse + 28, // 95: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.AggregateSelectionResponse + 29, // 96: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.AggregateSelectionElectraResponse + 32, // 97: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProof:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse + 32, // 98: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedAggregateSelectionProofElectra:output_type -> ethereum.eth.v1alpha1.SignedAggregateSubmitResponse + 24, // 99: ethereum.eth.v1alpha1.BeaconNodeValidator.ProposeExit:output_type -> ethereum.eth.v1alpha1.ProposeExitResponse + 83, // 100: ethereum.eth.v1alpha1.BeaconNodeValidator.SubscribeCommitteeSubnets:output_type -> google.protobuf.Empty + 38, // 101: ethereum.eth.v1alpha1.BeaconNodeValidator.CheckDoppelGanger:output_type -> ethereum.eth.v1alpha1.DoppelGangerResponse + 1, // 102: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncMessageBlockRoot:output_type -> ethereum.eth.v1alpha1.SyncMessageBlockRootResponse + 83, // 103: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSyncMessage:output_type -> google.protobuf.Empty + 4, // 104: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncSubcommitteeIndex:output_type -> ethereum.eth.v1alpha1.SyncSubcommitteeIndexResponse + 97, // 105: ethereum.eth.v1alpha1.BeaconNodeValidator.GetSyncCommitteeContribution:output_type -> ethereum.eth.v1alpha1.SyncCommitteeContribution + 83, // 106: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedContributionAndProof:output_type -> google.protobuf.Empty + 5, // 107: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamSlots:output_type -> ethereum.eth.v1alpha1.StreamSlotsResponse + 6, // 108: ethereum.eth.v1alpha1.BeaconNodeValidator.StreamBlocksAltair:output_type -> ethereum.eth.v1alpha1.StreamBlocksResponse + 83, // 109: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitValidatorRegistrations:output_type -> google.protobuf.Empty + 83, // 110: ethereum.eth.v1alpha1.BeaconNodeValidator.AssignValidatorToSubnet:output_type -> google.protobuf.Empty + 46, // 111: ethereum.eth.v1alpha1.BeaconNodeValidator.AggregatedSigAndAggregationBits:output_type -> ethereum.eth.v1alpha1.AggregatedSigAndAggregationBitsResponse + 60, // 112: ethereum.eth.v1alpha1.BeaconNodeValidator.GetExecutionPayloadEnvelope:output_type -> ethereum.eth.v1alpha1.ExecutionPayloadEnvelopeResponse + 83, // 113: ethereum.eth.v1alpha1.BeaconNodeValidator.PublishExecutionPayloadEnvelope:output_type -> google.protobuf.Empty + 98, // 114: ethereum.eth.v1alpha1.BeaconNodeValidator.PayloadAttestationData:output_type -> ethereum.eth.v1alpha1.PayloadAttestationData + 83, // 115: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitPayloadAttestation:output_type -> google.protobuf.Empty + 83, // 116: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedProposerPreferences:output_type -> google.protobuf.Empty + 83, // 117: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitBuilderPreferences:output_type -> google.protobuf.Empty + 83, // 118: ethereum.eth.v1alpha1.BeaconNodeValidator.SubmitSignedExecutionPayloadBid:output_type -> google.protobuf.Empty + 76, // [76:119] is the sub-list for method output_type + 33, // [33:76] is the sub-list for method input_type 33, // [33:33] is the sub-list for extension type_name 33, // [33:33] is the sub-list for extension extendee 0, // [0:33] is the sub-list for field type_name @@ -5783,6 +5796,8 @@ type BeaconNodeValidatorClient interface { // Deprecated: Do not use. SubmitSignedProposerPreferences(ctx context.Context, in *SubmitSignedProposerPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // Deprecated: Do not use. + SubmitBuilderPreferences(ctx context.Context, in *SubmitBuilderPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + // Deprecated: Do not use. SubmitSignedExecutionPayloadBid(ctx context.Context, in *SignedExecutionPayloadBid, opts ...grpc.CallOption) (*emptypb.Empty, error) } @@ -6296,6 +6311,16 @@ func (c *beaconNodeValidatorClient) SubmitSignedProposerPreferences(ctx context. return out, nil } +// Deprecated: Do not use. +func (c *beaconNodeValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *SubmitBuilderPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, "/ethereum.eth.v1alpha1.BeaconNodeValidator/SubmitBuilderPreferences", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Deprecated: Do not use. func (c *beaconNodeValidatorClient) SubmitSignedExecutionPayloadBid(ctx context.Context, in *SignedExecutionPayloadBid, opts ...grpc.CallOption) (*emptypb.Empty, error) { out := new(emptypb.Empty) @@ -6391,6 +6416,8 @@ type BeaconNodeValidatorServer interface { // Deprecated: Do not use. SubmitSignedProposerPreferences(context.Context, *SubmitSignedProposerPreferencesRequest) (*emptypb.Empty, error) // Deprecated: Do not use. + SubmitBuilderPreferences(context.Context, *SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) + // Deprecated: Do not use. SubmitSignedExecutionPayloadBid(context.Context, *SignedExecutionPayloadBid) (*emptypb.Empty, error) } @@ -6521,6 +6548,9 @@ func (*UnimplementedBeaconNodeValidatorServer) SubmitPayloadAttestation(context. func (*UnimplementedBeaconNodeValidatorServer) SubmitSignedProposerPreferences(context.Context, *SubmitSignedProposerPreferencesRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitSignedProposerPreferences not implemented") } +func (*UnimplementedBeaconNodeValidatorServer) SubmitBuilderPreferences(context.Context, *SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitBuilderPreferences not implemented") +} func (*UnimplementedBeaconNodeValidatorServer) SubmitSignedExecutionPayloadBid(context.Context, *SignedExecutionPayloadBid) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method SubmitSignedExecutionPayloadBid not implemented") } @@ -7279,6 +7309,24 @@ func _BeaconNodeValidator_SubmitSignedProposerPreferences_Handler(srv interface{ return interceptor(ctx, in, info, handler) } +func _BeaconNodeValidator_SubmitBuilderPreferences_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitBuilderPreferencesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BeaconNodeValidatorServer).SubmitBuilderPreferences(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/ethereum.eth.v1alpha1.BeaconNodeValidator/SubmitBuilderPreferences", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BeaconNodeValidatorServer).SubmitBuilderPreferences(ctx, req.(*SubmitBuilderPreferencesRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _BeaconNodeValidator_SubmitSignedExecutionPayloadBid_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SignedExecutionPayloadBid) if err := dec(in); err != nil { @@ -7449,6 +7497,10 @@ var _BeaconNodeValidator_serviceDesc = grpc.ServiceDesc{ MethodName: "SubmitSignedProposerPreferences", Handler: _BeaconNodeValidator_SubmitSignedProposerPreferences_Handler, }, + { + MethodName: "SubmitBuilderPreferences", + Handler: _BeaconNodeValidator_SubmitBuilderPreferences_Handler, + }, { MethodName: "SubmitSignedExecutionPayloadBid", Handler: _BeaconNodeValidator_SubmitSignedExecutionPayloadBid_Handler, diff --git a/proto/prysm/v1alpha1/validator.proto b/proto/prysm/v1alpha1/validator.proto index 59bd223a6132..5d5e89f1592a 100644 --- a/proto/prysm/v1alpha1/validator.proto +++ b/proto/prysm/v1alpha1/validator.proto @@ -542,6 +542,17 @@ service BeaconNodeValidator { }; } + // SubmitBuilderPreferences forwards a proposer's signed builder preferences to + // one builder ahead of a proposal slot (builder-specs preferences channel). + rpc SubmitBuilderPreferences(SubmitBuilderPreferencesRequest) + returns (google.protobuf.Empty) { + option deprecated = true; + option (google.api.http) = { + post : "/eth/v1alpha1/validator/builder_preferences" + body : "*" + }; + } + // SubmitSignedExecutionPayloadBid broadcasts a signed execution payload bid // to the P2P gossip network. rpc SubmitSignedExecutionPayloadBid(SignedExecutionPayloadBid) diff --git a/testing/mock/beacon_validator_client_mock.go b/testing/mock/beacon_validator_client_mock.go index 4573c0855caa..9368faf4bbfe 100644 --- a/testing/mock/beacon_validator_client_mock.go +++ b/testing/mock/beacon_validator_client_mock.go @@ -624,6 +624,26 @@ func (mr *MockBeaconNodeValidatorClientMockRecorder) SubmitAggregateSelectionPro return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProofElectra", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).SubmitAggregateSelectionProofElectra), varargs...) } +// SubmitBuilderPreferences mocks base method. +func (m *MockBeaconNodeValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *eth.SubmitBuilderPreferencesRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + m.ctrl.T.Helper() + varargs := []any{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "SubmitBuilderPreferences", varargs...) + ret0, _ := ret[0].(*emptypb.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitBuilderPreferences indicates an expected call of SubmitBuilderPreferences. +func (mr *MockBeaconNodeValidatorClientMockRecorder) SubmitBuilderPreferences(ctx, in any, opts ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBuilderPreferences", reflect.TypeOf((*MockBeaconNodeValidatorClient)(nil).SubmitBuilderPreferences), varargs...) +} + // SubmitPayloadAttestation mocks base method. func (m *MockBeaconNodeValidatorClient) SubmitPayloadAttestation(ctx context.Context, in *eth.PayloadAttestationMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { m.ctrl.T.Helper() diff --git a/testing/mock/beacon_validator_server_mock.go b/testing/mock/beacon_validator_server_mock.go index 02cc8a4bd796..8863fc6ba72c 100644 --- a/testing/mock/beacon_validator_server_mock.go +++ b/testing/mock/beacon_validator_server_mock.go @@ -476,6 +476,21 @@ func (mr *MockBeaconNodeValidatorServerMockRecorder) SubmitAggregateSelectionPro return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProofElectra", reflect.TypeOf((*MockBeaconNodeValidatorServer)(nil).SubmitAggregateSelectionProofElectra), arg0, arg1) } +// SubmitBuilderPreferences mocks base method. +func (m *MockBeaconNodeValidatorServer) SubmitBuilderPreferences(arg0 context.Context, arg1 *eth.SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubmitBuilderPreferences", arg0, arg1) + ret0, _ := ret[0].(*emptypb.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitBuilderPreferences indicates an expected call of SubmitBuilderPreferences. +func (mr *MockBeaconNodeValidatorServerMockRecorder) SubmitBuilderPreferences(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBuilderPreferences", reflect.TypeOf((*MockBeaconNodeValidatorServer)(nil).SubmitBuilderPreferences), arg0, arg1) +} + // SubmitPayloadAttestation mocks base method. func (m *MockBeaconNodeValidatorServer) SubmitPayloadAttestation(arg0 context.Context, arg1 *eth.PayloadAttestationMessage) (*emptypb.Empty, error) { m.ctrl.T.Helper() diff --git a/testing/validator-mock/validator_client_mock.go b/testing/validator-mock/validator_client_mock.go index 75c32c4e7fc0..eb2b55a62280 100644 --- a/testing/validator-mock/validator_client_mock.go +++ b/testing/validator-mock/validator_client_mock.go @@ -443,6 +443,21 @@ func (mr *MockValidatorClientMockRecorder) SubmitAggregateSelectionProofElectra( return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitAggregateSelectionProofElectra", reflect.TypeOf((*MockValidatorClient)(nil).SubmitAggregateSelectionProofElectra), ctx, in, arg2, arg3) } +// SubmitBuilderPreferences mocks base method. +func (m *MockValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *eth.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SubmitBuilderPreferences", ctx, in) + ret0, _ := ret[0].(*empty.Empty) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// SubmitBuilderPreferences indicates an expected call of SubmitBuilderPreferences. +func (mr *MockValidatorClientMockRecorder) SubmitBuilderPreferences(ctx, in any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SubmitBuilderPreferences", reflect.TypeOf((*MockValidatorClient)(nil).SubmitBuilderPreferences), ctx, in) +} + // SubmitPayloadAttestation mocks base method. func (m *MockValidatorClient) SubmitPayloadAttestation(ctx context.Context, in *eth.PayloadAttestationMessage) (*empty.Empty, error) { m.ctrl.T.Helper() diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index ae65443225d2..b0a785284307 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -300,6 +300,12 @@ func (c *beaconApiValidatorClient) SubmitSignedProposerPreferences(ctx context.C }) } +// TODO(gloas): no beacon-API endpoint exists for the builder preferences forward yet. +func (c *beaconApiValidatorClient) SubmitBuilderPreferences(_ context.Context, _ *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { + log.Debug("SubmitBuilderPreferences not yet implemented for beacon API client, skipping") + return new(empty.Empty), nil +} + // TODO(gloas): Wire up actual REST call to POST /eth/v2/beacon/execution_payload/bid func (c *beaconApiValidatorClient) SubmitSignedExecutionPayloadBid(_ context.Context, _ *ethpb.SignedExecutionPayloadBid) (*empty.Empty, error) { log.Debug("SubmitSignedExecutionPayloadBid not yet implemented for beacon API client, skipping") diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index 921a3dab2572..928e61e545a0 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -304,6 +304,9 @@ func (c *grpcValidatorClient) SubmitSignedProposerPreferences(ctx context.Contex return c.getClient().SubmitSignedProposerPreferences(ctx, in) } +func (c *grpcValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { + return c.getClient().SubmitBuilderPreferences(ctx, in) +} func (c *grpcValidatorClient) SubmitSignedExecutionPayloadBid(ctx context.Context, in *ethpb.SignedExecutionPayloadBid) (*empty.Empty, error) { return c.getClient().SubmitSignedExecutionPayloadBid(ctx, in) diff --git a/validator/client/iface/validator_client.go b/validator/client/iface/validator_client.go index 09d30755111a..bd4775fbc009 100644 --- a/validator/client/iface/validator_client.go +++ b/validator/client/iface/validator_client.go @@ -158,6 +158,7 @@ type ValidatorClient interface { // proposal slots. This replaces PrepareBeaconProposer and SubmitValidatorRegistrations // for Gloas+. SubmitSignedProposerPreferences(ctx context.Context, in *ethpb.SubmitSignedProposerPreferencesRequest) (*empty.Empty, error) + SubmitBuilderPreferences(ctx context.Context, in *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) SubmitSignedExecutionPayloadBid(ctx context.Context, in *ethpb.SignedExecutionPayloadBid) (*empty.Empty, error) StartEventStream(ctx context.Context, topics []string, eventsChannel chan<- *event.Event) EventStreamIsRunning() bool diff --git a/validator/client/request_auth_test.go b/validator/client/request_auth_test.go index 700a5124a7da..8dc81d0b0116 100644 --- a/validator/client/request_auth_test.go +++ b/validator/client/request_auth_test.go @@ -219,6 +219,40 @@ func uint64ValPtrClient(v uint64) *validatortypes.Uint64 { return &u } +// Warm both signs auths for the inline path and emits the ahead-of-time +// builder-specs preference submissions, pairing each builder URL with the auth +// signed over ITS auth_data and its own max payment. +func TestWarmBuilderRequestAuthsForDuties_EmitsPreferenceSubmissions(t *testing.T) { + kp := randKeypair(t) + km := newMockKeymanager(t, kp) + slot := primitives.Slot(30) + maxA := validatortypes.Uint64(100) + maxB := validatortypes.Uint64(200) + + v := validator{proposerSettings: settingsWithBuilders(kp.pub, + &proposer.BuilderEntry{URL: "https://a", AuthData: []byte("A"), MaxExecutionPayment: &maxA}, + &proposer.BuilderEntry{URL: "https://b", AuthData: []byte("B"), MaxExecutionPayment: &maxB}, + )} + duties := func(yield func(pubkey, *ethpb.ValidatorDuty) bool) { + // One past slot (skipped) and one future proposal slot. + yield(kp.pub, ðpb.ValidatorDuty{ProposerSlots: []primitives.Slot{slot, slot + 2}}) + } + + reqs := v.warmBuilderRequestAuthsForDuties(t.Context(), km, slot, duties) + require.Equal(t, 2, len(reqs)) + + byURL := map[string]*ethpb.SubmitBuilderPreferencesRequest{} + for _, r := range reqs { + byURL[r.Url] = r + require.DeepEqual(t, kp.pub[:], r.ValidatorPubkey) + require.Equal(t, slot+2, r.Request.Auth.Message.Slot) + } + require.DeepEqual(t, []byte("A"), byURL["https://a"].Request.Auth.Message.Data) + require.Equal(t, primitives.Gwei(100), byURL["https://a"].Request.Preferences.MaxExecutionPayment) + require.DeepEqual(t, []byte("B"), byURL["https://b"].Request.Auth.Message.Data) + require.Equal(t, primitives.Gwei(200), byURL["https://b"].Request.Preferences.MaxExecutionPayment) +} + func TestSignedRequestAuthFor_KeyIncludesAuthData(t *testing.T) { pk := [fieldparams.BLSPubkeyLength]byte{3} slot := primitives.Slot(4) diff --git a/validator/client/validator.go b/validator/client/validator.go index 73bb3413ed54..d93d88c404be 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -886,7 +886,15 @@ func (v *validator) PushProposerSettings(ctx context.Context, slot primitives.Sl v.connTracker.confirm(proposerPrefsPush, connGen) } - v.warmBuilderRequestAuths(ctx, km, slot) + if reqs := v.warmBuilderRequestAuths(ctx, km, slot); len(reqs) > 0 { + delay := params.BeaconConfig().SlotDuration() / 2 + time.AfterFunc(delay, func() { + // Detached from the slot context, which may expire before the delay elapses. + subCtx, cancel := context.WithTimeout(context.Background(), delay) + defer cancel() + v.submitBuilderPreferenceRequests(subCtx, reqs) + }) + } // TODO: figure out what to do post gloas for builder apis if !isPreGloas { @@ -1428,22 +1436,24 @@ func uint64Ptr(v *validatortypes.Uint64) *uint64 { // warmBuilderRequestAuths pre-signs the builder request auths for upcoming // proposal slots so proposeBlock can attach them inline (JIT) without signing on -// the hot path. It no longer submits to the beacon node: preferences now travel -// with the block request. -func (v *validator) warmBuilderRequestAuths(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) { +// the hot path, and returns the ahead-of-time submissions for the builder-specs +// preferences channel. Rebuilt every push so builders learn preferences ahead of +// the slot even across beacon node restarts. +func (v *validator) warmBuilderRequestAuths(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) []*ethpb.SubmitBuilderPreferencesRequest { if slots.ToEpoch(slot)+1 < params.BeaconConfig().GloasForkEpoch { - return + return nil } snap := v.duties.snapshot() if !snap.isInitialized() { - return + return nil } v.pruneSignedRequestAuths(slot) - v.warmBuilderRequestAuthsForDuties(ctx, km, slot, snap.currentDuties()) - v.warmBuilderRequestAuthsForDuties(ctx, km, slot, snap.nextDuties()) + reqs := v.warmBuilderRequestAuthsForDuties(ctx, km, slot, snap.currentDuties()) + return append(reqs, v.warmBuilderRequestAuthsForDuties(ctx, km, slot, snap.nextDuties())...) } -func (v *validator) warmBuilderRequestAuthsForDuties(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, duties iter.Seq2[pubkey, *ethpb.ValidatorDuty]) { +func (v *validator) warmBuilderRequestAuthsForDuties(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot, duties iter.Seq2[pubkey, *ethpb.ValidatorDuty]) []*ethpb.SubmitBuilderPreferencesRequest { + var reqs []*ethpb.SubmitBuilderPreferencesRequest for pk, duty := range duties { targets := v.builderTargetsForKey(pk) if len(targets) == 0 { @@ -1454,12 +1464,31 @@ func (v *validator) warmBuilderRequestAuthsForDuties(ctx context.Context, km key continue } for _, t := range targets { - if _, err := v.signRequestAuthCached(ctx, km, pk, t.url, t.authData, proposalSlot); err != nil { + signed, err := v.signRequestAuthCached(ctx, km, pk, t.url, t.authData, proposalSlot) + if err != nil { log.WithError(err).Warn("Failed to sign builder request auth") + continue } + reqs = append(reqs, ðpb.SubmitBuilderPreferencesRequest{ + ValidatorPubkey: pk[:], + Url: t.url, + Request: ðpb.BuilderPreferencesRequestV1{ + Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(t.maxPayment)}, + Auth: signed, + }, + }) } } } + return reqs +} + +func (v *validator) submitBuilderPreferenceRequests(ctx context.Context, reqs []*ethpb.SubmitBuilderPreferencesRequest) { + for _, req := range reqs { + if _, err := v.validatorClient.SubmitBuilderPreferences(ctx, req); err != nil { + log.WithError(err).Warn("Failed to submit builder preferences") + } + } } // buildBuilderPreferencesForSlot assembles the per-builder preferences to carry diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index 09298dda08e0..e3ad6e1492b6 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -18,10 +18,10 @@ import ( "github.com/pkg/errors" ) +// Per-key statuses; not_found is unused because Prysm supports pre-provisioning. const ( - configStatusSet = "set" - configStatusNotFound = "not_found" - configStatusError = "error" + configStatusSet = "set" + configStatusError = "error" ) // GetValidatorConfig implements GET /eth/v1/validator/config from keymanager-APIs #87. @@ -72,10 +72,9 @@ func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { return } + // Unrecognized fields are ignored for forward compatibility, per the spec. var req SetValidatorConfigRequest - dec := json.NewDecoder(r.Body) - dec.DisallowUnknownFields() - if err := dec.Decode(&req); err != nil { + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) return } @@ -84,21 +83,6 @@ func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { return } - km, err := s.validatorService.Keymanager() - if err != nil { - httputil.HandleError(w, err.Error(), http.StatusInternalServerError) - return - } - known, err := km.FetchValidatingPublicKeys(ctx) - if err != nil { - httputil.HandleError(w, err.Error(), http.StatusInternalServerError) - return - } - knownSet := make(map[[fieldparams.BLSPubkeyLength]byte]bool, len(known)) - for _, pk := range known { - knownSet[pk] = true - } - s.proposerSettingsLock.Lock() defer s.proposerSettingsLock.Unlock() @@ -121,10 +105,8 @@ func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusError, Message: err.Error()} continue } - if !knownSet[pubkey] { - resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusNotFound} - continue - } + // Keys the server does not yet manage are stored anyway (pre-provisioning + // per the spec); the config takes effect when the key is later added. if cfg == nil || cfg.isEmpty() { delete(settings.ProposeConfig, pubkey) resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusSet} diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index 61ac8130ca1c..ac560e2238a5 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -144,7 +144,9 @@ func TestServer_SetValidatorConfig_EmptyDocumentClearsKey(t *testing.T) { require.Equal(t, false, present) } -func TestServer_SetValidatorConfig_UnknownPubkeyNotFound(t *testing.T) { +// Configs for keys the server does not yet manage are stored (pre-provisioning +// per the spec) and take effect when the key is later added. +func TestServer_SetValidatorConfig_UnknownPubkeyPreProvisions(t *testing.T) { srv, _ := setupConfigServer(t, 1) // 48-byte pubkey never recovered into the keymanager. unknown := "0x" + "abcdef0011223344556677889900aabbccddeeff00112233445566778899aabbccddeeff001122334455667788990011" @@ -154,24 +156,31 @@ func TestServer_SetValidatorConfig_UnknownPubkeyNotFound(t *testing.T) { setResp := &SetValidatorConfigResponse{} require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) require.NotNil(t, setResp.Data[unknown]) - require.Equal(t, configStatusNotFound, setResp.Data[unknown].Status) + require.Equal(t, configStatusSet, setResp.Data[unknown].Status) - // Settings are untouched: no per-key config was written. getResp := getValidatorConfig(t, srv, "") - require.Equal(t, 0, len(getResp.Data.Configs)) + cfg := getResp.Data.Configs[unknown] + require.NotNil(t, cfg) + require.NotNil(t, cfg.Graffiti) + require.Equal(t, "x", *cfg.Graffiti) } -func TestServer_SetValidatorConfig_UnknownJSONFieldRejected(t *testing.T) { +// Unrecognized fields are ignored for forward compatibility, per the spec. +func TestServer_SetValidatorConfig_UnknownJSONFieldIgnored(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) - // Unknown field in the per-key config document. - w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"not_a_field":"x"}}}`) - require.Equal(t, http.StatusBadRequest, w.Code) + // Unknown fields in the per-key document and the envelope are both ignored; + // recognized fields alongside them are still applied. + w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"not_a_field":"x","graffiti":"kept"}},"bogus":true}`) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusSet, setResp.Data[pk].Status) - // Unknown field in the top-level envelope. - w = postValidatorConfig(t, srv, `{"configs":{},"bogus":true}`) - require.Equal(t, http.StatusBadRequest, w.Code) + getResp := getValidatorConfig(t, srv, "") + require.NotNil(t, getResp.Data.Configs[pk]) + require.Equal(t, "kept", *getResp.Data.Configs[pk].Graffiti) } func TestServer_SetValidatorConfig_PerKeyErrorsIsolated(t *testing.T) { From 10d4b2ec44d935c60260984eefb51a27f7d05d52 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 10:35:41 -0500 Subject: [PATCH 07/19] pocs implementing changes from https://github.com/ethereum/beacon-APIs/pull/630 --- api/client/builder/client.go | 4 +- api/headers.go | 1 + .../blockchain/execution_engine_test.go | 2 +- beacon-chain/rpc/endpoints.go | 9 + beacon-chain/rpc/endpoints_test.go | 1 + beacon-chain/rpc/eth/beacon/handlers.go | 11 +- beacon-chain/rpc/eth/validator/BUILD.bazel | 1 + .../rpc/eth/validator/builder_preferences.go | 185 +- .../eth/validator/builder_preferences_test.go | 55 + .../rpc/eth/validator/handlers_block_gloas.go | 3 + .../v1alpha1/validator/builder_preferences.go | 6 +- .../rpc/prysm/v1alpha1/validator/proposer.go | 7 +- .../prysm/v1alpha1/validator/proposer_bid.go | 21 +- .../v1alpha1/validator/proposer_gloas.go | 6 +- .../rpc/prysm/v1alpha1/validator/server.go | 5 - .../proposer/effective_builder_config_test.go | 4 +- config/proposer/loader/loader.go | 3 +- config/proposer/settings.go | 5 +- proto/prysm/v1alpha1/beacon_block.pb.go | 2278 +++++++++-------- proto/prysm/v1alpha1/beacon_block.proto | 6 + proto/prysm/v1alpha1/gloas_builder_api.proto | 15 +- .../beacon-api/beacon_api_validator_client.go | 11 +- .../client/beacon-api/builder_preferences.go | 78 +- .../client/beacon-api/get_beacon_block.go | 6 +- .../client/beacon-api/propose_beacon_block.go | 4 + validator/client/log_test.go | 2 +- validator/client/propose.go | 3 + validator/client/request_auth.go | 3 +- validator/client/validator.go | 9 +- validator/rpc/handlers_keymanager.go | 3 +- validator/rpc/handlers_validator_config.go | 5 +- 31 files changed, 1475 insertions(+), 1277 deletions(-) create mode 100644 beacon-chain/rpc/eth/validator/builder_preferences_test.go diff --git a/api/client/builder/client.go b/api/client/builder/client.go index 98fa6838914f..e57249e2bf3e 100644 --- a/api/client/builder/client.go +++ b/api/client/builder/client.go @@ -227,7 +227,7 @@ func (c *Client) doWithStatus(ctx context.Context, method string, path string, b u := c.baseURL.ResolveReference(&url.URL{Path: path}) - span.SetAttributes(trace.StringAttribute("url", u.String()), + span.SetAttributes(trace.StringAttribute("url", u.Redacted()), trace.StringAttribute("method", method)) req, err := http.NewRequestWithContext(ctx, method, u.String(), body) @@ -794,7 +794,7 @@ func unexpectedStatusErr(response *http.Response, expected []int) error { } else { body = "response body:\n" + string(bodyBytes) } - msg := fmt.Sprintf("expected=%v, got=%d, url=%s, body=%s", expected, response.StatusCode, response.Request.URL, body) + msg := fmt.Sprintf("expected=%v, got=%d, url=%s, body=%s", expected, response.StatusCode, response.Request.URL.Redacted(), body) var sentinel error switch response.StatusCode { diff --git a/api/headers.go b/api/headers.go index 0f0a1977d55f..7bb9be93809e 100644 --- a/api/headers.go +++ b/api/headers.go @@ -4,6 +4,7 @@ import "net/http" const ( VersionHeader = "Eth-Consensus-Version" + EthBuilderUrlHeader = "Eth-Builder-Url" ExecutionPayloadBlindedHeader = "Eth-Execution-Payload-Blinded" ExecutionPayloadValueHeader = "Eth-Execution-Payload-Value" ConsensusBlockValueHeader = "Eth-Consensus-Block-Value" diff --git a/beacon-chain/blockchain/execution_engine_test.go b/beacon-chain/blockchain/execution_engine_test.go index 9399a4a82512..104f2172621a 100644 --- a/beacon-chain/blockchain/execution_engine_test.go +++ b/beacon-chain/blockchain/execution_engine_test.go @@ -878,7 +878,7 @@ func Test_GetPayloadAttribute_GloasNoPreferenceFallback(t *testing.T) { require.NoError(t, err) service.cfg.SubscribedValidatorsCache.Add(proposerIdx) slot := primitives.Slot(1) - service.cfg.PayloadIDCache.Set(slot, [32]byte{},true, [8]byte{}) + service.cfg.PayloadIDCache.Set(slot, [32]byte{}, true, [8]byte{}) attr := service.getPayloadAttribute(ctx, st, slot, params.BeaconConfig().ZeroHash[:], false) require.Equal(t, false, attr.IsEmpty()) diff --git a/beacon-chain/rpc/endpoints.go b/beacon-chain/rpc/endpoints.go index ccf07cdb14a9..904418899e6f 100644 --- a/beacon-chain/rpc/endpoints.go +++ b/beacon-chain/rpc/endpoints.go @@ -429,6 +429,15 @@ func (s *Service) validatorEndpoints( handler: server.BeaconCommitteeSelections, methods: []string{http.MethodPost}, }, + { + template: "/eth/v1/validator/builder_preferences/{pubkey}", + name: namespace + ".SubmitBuilderPreferences", + middleware: []middleware.Middleware{ + middleware.ContentTypeHandler([]string{api.JsonMediaType}), + }, + handler: server.SubmitBuilderPreferences, + methods: []string{http.MethodPost}, + }, { template: "/eth/v1/validator/sync_committee_selections", name: namespace + ".SyncCommittee Selections", diff --git a/beacon-chain/rpc/endpoints_test.go b/beacon-chain/rpc/endpoints_test.go index 239611b7ac8a..6f3826297d3e 100644 --- a/beacon-chain/rpc/endpoints_test.go +++ b/beacon-chain/rpc/endpoints_test.go @@ -110,6 +110,7 @@ func Test_endpoints(t *testing.T) { "/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost}, "/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost}, "/eth/v1/validator/beacon_committee_selections": {http.MethodPost}, + "/eth/v1/validator/builder_preferences/{pubkey}": {http.MethodPost}, "/eth/v1/validator/sync_committee_selections": {http.MethodPost}, "/eth/v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}": {http.MethodGet}, "/eth/v1/validator/sync_committee_contribution": {http.MethodGet}, diff --git a/beacon-chain/rpc/eth/beacon/handlers.go b/beacon-chain/rpc/eth/beacon/handlers.go index 231df515091b..ef962df28409 100644 --- a/beacon-chain/rpc/eth/beacon/handlers.go +++ b/beacon-chain/rpc/eth/beacon/handlers.go @@ -415,7 +415,7 @@ func (s *Server) publishBlindedBlockSSZ(ctx context.Context, w http.ResponseWrit httputil.HandleError(w, err.Error(), http.StatusBadRequest) return } - s.proposeBlock(ctx, w, genericBlock) + s.proposeBlock(ctx, w, r, genericBlock) } // decodeBlindedBlockSSZ dispatches to the correct SSZ decoder based on versionHeader. @@ -520,7 +520,7 @@ func (s *Server) publishBlindedBlock(ctx context.Context, w http.ResponseWriter, httputil.HandleError(w, err.Error(), http.StatusBadRequest) return } - s.proposeBlock(ctx, w, genericBlock) + s.proposeBlock(ctx, w, r, genericBlock) } // decodeBlindedBlockJSON dispatches to the correct JSON decoder based on versionHeader. @@ -641,7 +641,7 @@ func (s *Server) publishBlockSSZ(ctx context.Context, w http.ResponseWriter, r * return } - s.proposeBlock(ctx, w, genericBlock) + s.proposeBlock(ctx, w, r, genericBlock) } var sszDecoders = map[string]blockDecoder{ @@ -809,7 +809,7 @@ func (s *Server) publishBlock(ctx context.Context, w http.ResponseWriter, r *htt return } - s.proposeBlock(ctx, w, genericBlock) + s.proposeBlock(ctx, w, r, genericBlock) } var jsonDecoders = map[string]blockDecoder{ @@ -901,7 +901,8 @@ func broadcastSidecarsIfSupported(ctx context.Context, s *Server, b interfaces.S } } -func (s *Server) proposeBlock(ctx context.Context, w http.ResponseWriter, blk *eth.GenericSignedBeaconBlock) { +func (s *Server) proposeBlock(ctx context.Context, w http.ResponseWriter, r *http.Request, blk *eth.GenericSignedBeaconBlock) { + blk.BuilderUrl = r.Header.Get(api.EthBuilderUrlHeader) _, err := s.V1Alpha1ValidatorServer.ProposeBeaconBlock(ctx, blk) if err != nil { httputil.HandleError(w, err.Error(), http.StatusInternalServerError) diff --git a/beacon-chain/rpc/eth/validator/BUILD.bazel b/beacon-chain/rpc/eth/validator/BUILD.bazel index 9b461f5771af..ae06df73f400 100644 --- a/beacon-chain/rpc/eth/validator/BUILD.bazel +++ b/beacon-chain/rpc/eth/validator/BUILD.bazel @@ -60,6 +60,7 @@ go_library( go_test( name = "go_default_test", srcs = [ + "builder_preferences_test.go", "handlers_block_gloas_test.go", "handlers_block_test.go", "handlers_test.go", diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index 3c0c68d72365..a5e7795ff756 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -2,31 +2,36 @@ package validator import ( "encoding/json" + "fmt" "io" "net/http" "strconv" + "strings" + "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/shared" + fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" + "github.com/OffchainLabs/prysm/v7/network/httputil" eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" ) -// produceBlockV4Request is the POST body for the JIT produce-block endpoint -// (beacon-APIs #625): the proposer's per-builder preferences for this proposal. -type produceBlockV4Request struct { - BuilderPreferences []*builderPreferenceJson `json:"builder_preferences"` +// builderEntryJson is one element of the produceBlockV4 POST body: a bare array +// of per-builder entries (beacon-APIs #630). proxy and pubkey are accepted but +// currently unused. +type builderEntryJson struct { + Url string `json:"url"` + Proxy string `json:"proxy,omitempty"` + Pubkey string `json:"pubkey,omitempty"` + Auth *signedAuthJson `json:"auth"` + MaxExecutionPayment string `json:"max_execution_payment,omitempty"` + MinBid string `json:"min_bid,omitempty"` + BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` } -type builderPreferenceJson struct { - Url string `json:"url"` - SignedRequestAuth *signedRequestAuthJson `json:"signed_request_auth"` - MaxExecutionPayment string `json:"max_execution_payment"` - MinBid string `json:"min_bid,omitempty"` - BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` -} - -type signedRequestAuthJson struct { +type signedAuthJson struct { Message *requestAuthJson `json:"message"` Signature string `json:"signature"` } @@ -36,64 +41,57 @@ type requestAuthJson struct { Slot string `json:"slot"` } -// parseBuilderPreferencesBody decodes the JIT builder-preferences POST body into -// consensus objects. An empty body yields no preferences. +// parseBuilderPreferencesBody decodes the JIT produce-block POST body. Malformed +// entries are ignored per the spec; an empty body yields no preferences. func parseBuilderPreferencesBody(r *http.Request) ([]*eth.BuilderPreferenceV1, error) { if r.Body == nil { return nil, nil } - var req produceBlockV4Request - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + var entries []*builderEntryJson + if err := json.NewDecoder(r.Body).Decode(&entries); err != nil { if errors.Is(err, io.EOF) { return nil, nil } return nil, errors.Wrap(err, "could not decode builder preferences") } - out := make([]*eth.BuilderPreferenceV1, 0, len(req.BuilderPreferences)) - for i, p := range req.BuilderPreferences { - conv, err := p.toConsensus(i) + out := make([]*eth.BuilderPreferenceV1, 0, len(entries)) + for _, p := range entries { + conv, err := p.toConsensus() if err != nil { - return nil, err + log.WithError(err).Debug("Ignoring malformed builder entry") + continue } out = append(out, conv) } return out, nil } -func (p *builderPreferenceJson) toConsensus(i int) (*eth.BuilderPreferenceV1, error) { - if p == nil || p.SignedRequestAuth == nil || p.SignedRequestAuth.Message == nil { - return nil, errors.Errorf("builder_preferences[%d] missing signed_request_auth", i) - } - maxPayment, err := strconv.ParseUint(p.MaxExecutionPayment, 10, 64) - if err != nil { - return nil, errors.Errorf("builder_preferences[%d].max_execution_payment is not a valid uint64", i) - } - data, err := hexutil.Decode(p.SignedRequestAuth.Message.Data) - if err != nil { - return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.message.data is not valid hex", i) +func (p *builderEntryJson) toConsensus() (*eth.BuilderPreferenceV1, error) { + if p == nil || p.Url == "" { + return nil, errors.New("missing url") } - slot, err := strconv.ParseUint(p.SignedRequestAuth.Message.Slot, 10, 64) - if err != nil { - return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.message.slot is not a valid uint64", i) + if p.Auth == nil || p.Auth.Message == nil { + return nil, errors.New("missing auth") } - sig, err := hexutil.Decode(p.SignedRequestAuth.Signature) + auth, err := p.Auth.toConsensus() if err != nil { - return nil, errors.Errorf("builder_preferences[%d].signed_request_auth.signature is not valid hex", i) + return nil, err } out := ð.BuilderPreferenceV1{ - Url: p.Url, - Request: ð.BuilderPreferencesRequestV1{ - Preferences: ð.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(maxPayment)}, - Auth: ð.SignedRequestAuthV1{ - Message: ð.RequestAuthV1{Data: data, Slot: primitives.Slot(slot)}, - Signature: sig, - }, - }, + Url: p.Url, + Request: ð.BuilderPreferencesRequestV1{Preferences: ð.BuilderPreferencesV1{}, Auth: auth}, + } + if p.MaxExecutionPayment != "" { + v, err := strconv.ParseUint(p.MaxExecutionPayment, 10, 64) + if err != nil { + return nil, errors.New("max_execution_payment is not a valid uint64") + } + out.Request.Preferences.MaxExecutionPayment = primitives.Gwei(v) } if p.MinBid != "" { v, err := strconv.ParseUint(p.MinBid, 10, 64) if err != nil { - return nil, errors.Errorf("builder_preferences[%d].min_bid is not a valid uint64", i) + return nil, errors.New("min_bid is not a valid uint64") } g := primitives.Gwei(v) out.MinBid = &g @@ -101,9 +99,102 @@ func (p *builderPreferenceJson) toConsensus(i int) (*eth.BuilderPreferenceV1, er if p.BuilderBoostFactor != "" { v, err := strconv.ParseUint(p.BuilderBoostFactor, 10, 64) if err != nil { - return nil, errors.Errorf("builder_preferences[%d].builder_boost_factor is not a valid uint64", i) + return nil, errors.New("builder_boost_factor is not a valid uint64") } out.BuilderBoostFactor = &v } return out, nil } + +func (a *signedAuthJson) toConsensus() (*eth.SignedRequestAuthV1, error) { + data, err := hexutil.Decode(a.Message.Data) + if err != nil { + return nil, errors.New("auth.message.data is not valid hex") + } + slot, err := strconv.ParseUint(a.Message.Slot, 10, 64) + if err != nil { + return nil, errors.New("auth.message.slot is not a valid uint64") + } + sig, err := hexutil.Decode(a.Signature) + if err != nil { + return nil, errors.New("auth.signature is not valid hex") + } + return ð.SignedRequestAuthV1{ + Message: ð.RequestAuthV1{Data: data, Slot: primitives.Slot(slot)}, + Signature: sig, + }, nil +} + +// builderPreferenceEntryJson is one element of the ahead-of-time preferences POST +// body (beacon-APIs #630): url, required auth, and the signed max payment. +type builderPreferenceEntryJson struct { + Url string `json:"url"` + Proxy string `json:"proxy,omitempty"` + Auth *signedAuthJson `json:"auth"` + MaxExecutionPayment string `json:"max_execution_payment"` +} + +// SubmitBuilderPreferences implements POST /eth/v1/validator/builder_preferences/{pubkey}: +// it forwards each entry's signed preferences to its builder ahead of the proposal slot. +func (s *Server) SubmitBuilderPreferences(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "validator.SubmitBuilderPreferences") + defer span.End() + + if shared.IsSyncing(ctx, w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) { + return + } + _, pubkey, ok := shared.HexFromRoute(w, r, "pubkey", fieldparams.BLSPubkeyLength) + if !ok { + return + } + var entries []*builderPreferenceEntryJson + if err := json.NewDecoder(r.Body).Decode(&entries); err != nil { + httputil.HandleError(w, "Could not decode builder preference entries: "+err.Error(), http.StatusBadRequest) + return + } + if len(entries) == 0 { + httputil.HandleError(w, "No builder preference entries submitted", http.StatusBadRequest) + return + } + + var failures []string + for i, e := range entries { + req, err := e.toSubmitRequest(pubkey) + if err == nil { + _, err = s.V1Alpha1Server.SubmitBuilderPreferences(ctx, req) + } + if err != nil { + failures = append(failures, fmt.Sprintf("entry %d: %v", i, err)) + } + } + if len(failures) > 0 { + httputil.HandleError(w, strings.Join(failures, "; "), http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusOK) +} + +func (e *builderPreferenceEntryJson) toSubmitRequest(pubkey []byte) (*eth.SubmitBuilderPreferencesRequest, error) { + if e == nil || e.Url == "" { + return nil, errors.New("missing url") + } + if e.Auth == nil || e.Auth.Message == nil { + return nil, errors.New("missing auth") + } + auth, err := e.Auth.toConsensus() + if err != nil { + return nil, err + } + maxPayment, err := strconv.ParseUint(e.MaxExecutionPayment, 10, 64) + if err != nil { + return nil, errors.New("max_execution_payment is not a valid uint64") + } + return ð.SubmitBuilderPreferencesRequest{ + ValidatorPubkey: pubkey, + Url: e.Url, + Request: ð.BuilderPreferencesRequestV1{ + Preferences: ð.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(maxPayment)}, + Auth: auth, + }, + }, nil +} diff --git a/beacon-chain/rpc/eth/validator/builder_preferences_test.go b/beacon-chain/rpc/eth/validator/builder_preferences_test.go new file mode 100644 index 000000000000..2065d4dc3cbb --- /dev/null +++ b/beacon-chain/rpc/eth/validator/builder_preferences_test.go @@ -0,0 +1,55 @@ +package validator + +import ( + "bytes" + "net/http/httptest" + "testing" + + "github.com/OffchainLabs/prysm/v7/testing/require" +) + +// The produce-block body is a bare BuilderEntry array; malformed entries are +// ignored per beacon-APIs #630 rather than failing the request. +func TestParseBuilderPreferencesBody(t *testing.T) { + validEntry := `{"url":"https://b","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"max_execution_payment":"1000","min_bid":"5","builder_boost_factor":"120"}` + + t.Run("valid entry parses with all fields", func(t *testing.T) { + r := httptest.NewRequest("POST", "/", bytes.NewBufferString(`[`+validEntry+`]`)) + prefs, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 1, len(prefs)) + p := prefs[0] + require.Equal(t, "https://b", p.Url) + require.Equal(t, uint64(1000), uint64(p.Request.Preferences.MaxExecutionPayment)) + require.Equal(t, uint64(5), uint64(*p.MinBid)) + require.Equal(t, uint64(120), *p.BuilderBoostFactor) + require.DeepEqual(t, []byte{1, 2}, p.Request.Auth.Message.Data) + }) + t.Run("malformed entries are skipped, valid ones kept", func(t *testing.T) { + body := `[{"url":""},{"url":"https://no-auth"},{"url":"https://bad","auth":{"message":{"data":"nothex","slot":"1"},"signature":"0x00"}},` + validEntry + `]` + r := httptest.NewRequest("POST", "/", bytes.NewBufferString(body)) + prefs, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 1, len(prefs)) + require.Equal(t, "https://b", prefs[0].Url) + }) + t.Run("empty body yields no preferences", func(t *testing.T) { + r := httptest.NewRequest("POST", "/", bytes.NewBuffer(nil)) + prefs, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 0, len(prefs)) + }) + t.Run("non-array body errors", func(t *testing.T) { + r := httptest.NewRequest("POST", "/", bytes.NewBufferString(`{"builder_preferences":[]}`)) + _, err := parseBuilderPreferencesBody(r) + require.NotNil(t, err) + }) +} + +func repeatHex(n int) string { + out := make([]byte, 0, n*2) + for i := 0; i < n; i++ { + out = append(out, '0', '9') + } + return string(out) +} diff --git a/beacon-chain/rpc/eth/validator/handlers_block_gloas.go b/beacon-chain/rpc/eth/validator/handlers_block_gloas.go index d419e40b768d..6255fb4fe8de 100644 --- a/beacon-chain/rpc/eth/validator/handlers_block_gloas.go +++ b/beacon-chain/rpc/eth/validator/handlers_block_gloas.go @@ -138,6 +138,9 @@ func (s *Server) ProduceBlockV4(w http.ResponseWriter, r *http.Request) { w.Header().Set(api.VersionHeader, version.String(version.Gloas)) w.Header().Set(api.ConsensusBlockValueHeader, consensusBlockValue) w.Header().Set(api.ExecutionPayloadIncludedHeader, fmt.Sprintf("%v", includePayload)) + if v1alpha1resp.BuilderUrl != "" { + w.Header().Set(api.EthBuilderUrlHeader, v1alpha1resp.BuilderUrl) + } isSSZ := httputil.RespondWithSsz(r) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go index 7810aa3c607c..52b9a5813494 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go @@ -11,10 +11,8 @@ import ( "google.golang.org/protobuf/types/known/emptypb" ) -// SubmitBuilderPreferences forwards a proposer's signed builder preferences to the -// named builder ahead of a proposal slot (builder-specs preferences channel). The -// beacon node is a pure conduit: bid-selection preferences travel inline with the -// block request instead. +// SubmitBuilderPreferences forwards a proposer's signed preferences to the named +// builder ahead of a proposal slot; bid-selection params travel with the block request. func (vs *Server) SubmitBuilderPreferences(ctx context.Context, req *ethpb.SubmitBuilderPreferencesRequest) (*emptypb.Empty, error) { ctx, span := trace.StartSpan(ctx, "ValidatorServer.SubmitBuilderPreferences") defer span.End() diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index 90995e7a0aed..23688111cbc0 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -347,10 +347,9 @@ func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSign } } - if block.Version() >= version.Gloas { - if src, builderURL := vs.bidSourceForSlot(block.Block().Slot()); src == bidSourceBuilderAPI { - go vs.submitBlockToBuilder(block, builderURL) - } + // Empty means self-build or a P2P bid; the builder learns those via gossip. + if block.Version() >= version.Gloas && req.BuilderUrl != "" { + go vs.submitBlockToBuilder(block, req.BuilderUrl) } if err := <-errChan; err != nil { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index 24af73ddc00a..28a233ca0e76 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -102,9 +102,8 @@ func (vs *Server) setExecutionPayloadBid( return bidSourceSelfBuild, nil } -// neutralBuilderBoostFactor is the 100% factor: builder bids compete on raw value. -// Callers must default a missing preference to this so an explicit 0 keeps its -// reserved "prefer local" meaning. +// The 100% factor. Callers default a missing preference to this so an explicit +// 0 keeps its reserved prefer-local meaning. const neutralBuilderBoostFactor = 100 // bidPreferences carries the proposer's per-key builder bid-selection preferences. @@ -304,22 +303,6 @@ func (vs *Server) proposerPreferenceForProposal(ctx context.Context, st state.Be return pref } -func (vs *Server) recordBidSource(slot primitives.Slot, src bidSource, builderURL string) { - vs.lastBidLock.Lock() - defer vs.lastBidLock.Unlock() - vs.lastBidSlot, vs.lastBidSource, vs.lastBidBuilderURL = slot, src, builderURL -} - -// Falls back to self-build when the record is for another slot. -func (vs *Server) bidSourceForSlot(slot primitives.Slot) (bidSource, string) { - vs.lastBidLock.Lock() - defer vs.lastBidLock.Unlock() - if vs.lastBidSlot != slot { - return bidSourceSelfBuild, "" - } - return vs.lastBidSource, vs.lastBidBuilderURL -} - // Best-effort and detached from the propose RPC, the builder also learns of the block via P2P. func (vs *Server) submitBlockToBuilder(block interfaces.ReadOnlySignedBeaconBlock, builderURL string) { if vs.BlockBuilder == nil || builderURL == "" { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go index 6ddcb9af806c..7864f6565e9a 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go @@ -35,6 +35,7 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea // local is our self-build candidate and the baseline for comparing incoming bids. var selfBuilt bool + var winningBuilderURL string local, err := vs.getLocalPayload(ctx, sBlk.Block(), head, parentFull) if err != nil { log.WithError(err).Warn("Could not get local payload, falling back to P2P bid") @@ -77,7 +78,9 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea if bidErr != nil { return nil, status.Errorf(codes.Internal, "Could not set execution payload bid: %v", bidErr) } - vs.recordBidSource(sBlk.Block().Slot(), src, builderURL) + if src == bidSourceBuilderAPI { + winningBuilderURL = builderURL + } selfBuilt = src == bidSourceSelfBuild } @@ -101,6 +104,7 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea if err != nil { return nil, err } + blk.BuilderUrl = winningBuilderURL // Eager (stateless) self-build: bundle envelope + blobs inline; stateful publishes from the cache. if eagerPayloadStateRoot && envelope != nil { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/server.go b/beacon-chain/rpc/prysm/v1alpha1/validator/server.go index 0018fa61b9f2..683fadadd3cd 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/server.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/server.go @@ -6,7 +6,6 @@ package validator import ( "bytes" "context" - "sync" "time" "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain" @@ -93,10 +92,6 @@ type Server struct { AttestationStateFetcher blockchain.AttestationStateFetcher NewExecutionPayloadBidVerifier verification.NewExecutionPayloadBidVerifier GraffitiInfo *execution.GraffitiInfo - lastBidLock sync.Mutex - lastBidSlot primitives.Slot - lastBidSource bidSource // Guarded by lastBidLock, set during Gloas block build, read when proposing. - lastBidBuilderURL string // Guarded by lastBidLock, winning Builder-API URL for lastBidSlot. } // Deprecated: The gRPC API will remain the default and fully supported through v8 (expected in 2026) but will be eventually removed in favor of REST API. diff --git a/config/proposer/effective_builder_config_test.go b/config/proposer/effective_builder_config_test.go index ba429e1efb39..e76ecc2a012a 100644 --- a/config/proposer/effective_builder_config_test.go +++ b/config/proposer/effective_builder_config_test.go @@ -107,8 +107,8 @@ func TestEffectiveBuilderConfig(t *testing.T) { perKey := &BuilderConfig{Enabled: proto.Bool(true)} require.Equal(t, 1, len(EffectiveBuilderConfig(perKey, def).Relays)) }) - // Builders and relays are independent fields: setting one per-key does not - // suppress inheriting the other. Documented composite, pinned deliberately. + // Builders and relays are independent fields; setting one per-key does not + // suppress inheriting the other. t.Run("per-key relays plus default builders compose", func(t *testing.T) { def := &BuilderConfig{Builders: []*BuilderEntry{entryA}} perKey := &BuilderConfig{Relays: []string{"https://r-key"}} diff --git a/config/proposer/loader/loader.go b/config/proposer/loader/loader.go index ecf93e11e373..c2664d34734e 100644 --- a/config/proposer/loader/loader.go +++ b/config/proposer/loader/loader.go @@ -290,8 +290,7 @@ func mergeProposerSettings(loaded, db *validatorpb.ProposerSettingsPayload, opti } // promotePayloadToV2 mirrors Settings.UpgradeToV2 plus legacy presence -// normalization at the payload level, so v1-shaped content merged into a v2 -// result is read correctly under v2 semantics. +// normalization, so v1 content merged into a v2 result reads correctly. func promotePayloadToV2(p *validatorpb.ProposerSettingsPayload) { promote := func(opt *validatorpb.ProposerOptionPayload) { if opt == nil || opt.Builder == nil { diff --git a/config/proposer/settings.go b/config/proposer/settings.go index da71ef6eb4d6..2e916114dda6 100644 --- a/config/proposer/settings.go +++ b/config/proposer/settings.go @@ -136,9 +136,8 @@ func (bc *BuilderConfig) IsEnabled() bool { return bc != nil && bc.Enabled != nil && *bc.Enabled } -// EffectiveBuilderConfig resolves a key's builder config with field-level -// inheritance from the default: fields unset on perKey inherit def's values, and -// the builders/relays lists replace (never merge) when present on perKey. +// EffectiveBuilderConfig resolves per-key builder config with field-level +// inheritance; the builders/relays lists replace rather than merge. func EffectiveBuilderConfig(perKey, def *BuilderConfig) *BuilderConfig { if perKey == nil { return def diff --git a/proto/prysm/v1alpha1/beacon_block.pb.go b/proto/prysm/v1alpha1/beacon_block.pb.go index 73908c51c1b0..132a5f8c59d1 100755 --- a/proto/prysm/v1alpha1/beacon_block.pb.go +++ b/proto/prysm/v1alpha1/beacon_block.pb.go @@ -44,6 +44,7 @@ type GenericSignedBeaconBlock struct { // *GenericSignedBeaconBlock_Gloas Block isGenericSignedBeaconBlock_Block `protobuf_oneof:"block"` IsBlinded bool `protobuf:"varint,100,opt,name=is_blinded,json=isBlinded,proto3" json:"is_blinded,omitempty"` + BuilderUrl string `protobuf:"bytes,102,opt,name=builder_url,json=builderUrl,proto3" json:"builder_url,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -209,6 +210,13 @@ func (x *GenericSignedBeaconBlock) GetIsBlinded() bool { return false } +func (x *GenericSignedBeaconBlock) GetBuilderUrl() string { + if x != nil { + return x.BuilderUrl + } + return "" +} + type isGenericSignedBeaconBlock_Block interface { isGenericSignedBeaconBlock_Block() } @@ -313,6 +321,7 @@ type GenericBeaconBlock struct { Block isGenericBeaconBlock_Block `protobuf_oneof:"block"` IsBlinded bool `protobuf:"varint,100,opt,name=is_blinded,json=isBlinded,proto3" json:"is_blinded,omitempty"` PayloadValue string `protobuf:"bytes,101,opt,name=payload_value,json=payloadValue,proto3" json:"payload_value,omitempty"` + BuilderUrl string `protobuf:"bytes,102,opt,name=builder_url,json=builderUrl,proto3" json:"builder_url,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -494,6 +503,13 @@ func (x *GenericBeaconBlock) GetPayloadValue() string { return "" } +func (x *GenericBeaconBlock) GetBuilderUrl() string { + if x != nil { + return x.BuilderUrl + } + return "" +} + type isGenericBeaconBlock_Block interface { isGenericBeaconBlock_Block() } @@ -4534,7 +4550,7 @@ var file_proto_prysm_v1alpha1_beacon_block_proto_rawDesc = []byte{ 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x97, 0x09, 0x0a, 0x18, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb8, 0x09, 0x0a, 0x18, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x42, 0x0a, 0x06, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, @@ -4606,554 +4622,306 @@ var file_proto_prysm_v1alpha1_beacon_block_proto_rawDesc = []byte{ 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x48, 0x00, 0x52, 0x05, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x3a, 0x02, 0x18, 0x01, 0x42, - 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4a, 0x04, 0x08, 0x65, 0x10, 0x66, 0x22, 0xbc, - 0x09, 0x0a, 0x12, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x3c, 0x0a, 0x06, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x06, 0x70, 0x68, 0x61, - 0x73, 0x65, 0x30, 0x12, 0x42, 0x0a, 0x06, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x48, 0x00, 0x52, - 0x06, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x4b, 0x0a, 0x09, 0x62, 0x65, 0x6c, 0x6c, 0x61, - 0x74, 0x72, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, - 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x48, 0x00, 0x52, 0x09, 0x62, 0x65, 0x6c, 0x6c, 0x61, - 0x74, 0x72, 0x69, 0x78, 0x12, 0x61, 0x0a, 0x11, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, - 0x62, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, - 0x72, 0x69, 0x78, 0x48, 0x00, 0x52, 0x10, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, - 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x45, 0x0a, 0x07, 0x63, 0x61, 0x70, 0x65, 0x6c, - 0x6c, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x66, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x3a, 0x02, 0x18, 0x01, + 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x4a, 0x04, 0x08, 0x65, 0x10, 0x66, 0x22, + 0xdd, 0x09, 0x0a, 0x12, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x69, 0x63, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x3c, 0x0a, 0x06, 0x70, 0x68, 0x61, 0x73, 0x65, 0x30, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x06, 0x70, 0x68, + 0x61, 0x73, 0x65, 0x30, 0x12, 0x42, 0x0a, 0x06, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x48, 0x00, + 0x52, 0x06, 0x61, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x4b, 0x0a, 0x09, 0x62, 0x65, 0x6c, 0x6c, + 0x61, 0x74, 0x72, 0x69, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, + 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x48, 0x00, 0x52, 0x09, 0x62, 0x65, 0x6c, 0x6c, + 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x61, 0x0a, 0x11, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x5f, 0x62, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, + 0x74, 0x72, 0x69, 0x78, 0x48, 0x00, 0x52, 0x10, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, + 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x45, 0x0a, 0x07, 0x63, 0x61, 0x70, 0x65, + 0x6c, 0x6c, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, + 0x65, 0x6c, 0x6c, 0x61, 0x48, 0x00, 0x52, 0x07, 0x63, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, + 0x5b, 0x0a, 0x0f, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x70, 0x65, 0x6c, + 0x6c, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, - 0x6c, 0x6c, 0x61, 0x48, 0x00, 0x52, 0x07, 0x63, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x5b, - 0x0a, 0x0f, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x70, 0x65, 0x6c, 0x6c, - 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x48, 0x00, 0x52, 0x0e, 0x62, 0x6c, 0x69, - 0x6e, 0x64, 0x65, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x47, 0x0a, 0x05, 0x64, - 0x65, 0x6e, 0x65, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x48, 0x00, 0x52, 0x05, 0x64, - 0x65, 0x6e, 0x65, 0x62, 0x12, 0x55, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, - 0x64, 0x65, 0x6e, 0x65, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, + 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x48, 0x00, 0x52, 0x0e, 0x62, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x47, 0x0a, 0x05, + 0x64, 0x65, 0x6e, 0x65, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x48, 0x00, 0x52, 0x0c, 0x62, - 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x4d, 0x0a, 0x07, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x48, - 0x00, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x5b, 0x0a, 0x0f, 0x62, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x18, 0x0a, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, - 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, - 0x65, 0x63, 0x74, 0x72, 0x61, 0x48, 0x00, 0x52, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, - 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x44, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x75, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, - 0x73, 0x46, 0x75, 0x6c, 0x75, 0x48, 0x00, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x75, 0x12, 0x52, 0x0a, - 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x75, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, - 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, - 0x6c, 0x75, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x46, 0x75, 0x6c, - 0x75, 0x12, 0x3f, 0x0a, 0x05, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x48, 0x00, 0x52, 0x05, 0x67, 0x6c, 0x6f, - 0x61, 0x73, 0x12, 0x58, 0x0a, 0x0e, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x74, - 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x67, - 0x6c, 0x6f, 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x69, 0x73, 0x5f, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x65, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x18, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x73, 0x0a, - 0x11, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x38, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0xea, 0x02, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, - 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, - 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, - 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, - 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, - 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, - 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, - 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, - 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, - 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x6f, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, - 0xd1, 0x04, 0x0a, 0x0f, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, - 0x6f, 0x64, 0x79, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, - 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, - 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, - 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, - 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, - 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, - 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, - 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, - 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, - 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, - 0x69, 0x74, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x10, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, - 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x4e, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x4e, 0x0a, 0x0d, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x22, 0xad, 0x01, 0x0a, 0x12, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x65, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x35, 0x0a, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e, 0x64, - 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x42, 0x08, 0x92, 0xb5, 0x18, 0x04, - 0x32, 0x30, 0x34, 0x38, 0x52, 0x10, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, - 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, 0x64, 0x61, - 0x74, 0x61, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x76, 0x0a, 0x1e, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x31, 0x12, 0x50, 0x0a, 0x08, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x65, + 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x48, 0x00, 0x52, 0x05, + 0x64, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x55, 0x0a, 0x0d, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x5f, 0x64, 0x65, 0x6e, 0x65, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x56, 0x31, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x3a, 0x02, 0x18, 0x01, - 0x22, 0x8f, 0x01, 0x0a, 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x56, 0x31, 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x56, 0x31, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, - 0x72, 0x65, 0x22, 0xa1, 0x01, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x2b, - 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, 0x0c, 0x66, - 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x67, - 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, - 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, - 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x75, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x8e, 0x01, - 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x12, 0x42, 0x0a, 0x06, - 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, - 0x12, 0x1c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, - 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x7f, - 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, - 0x69, 0x72, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, - 0xf6, 0x02, 0x0a, 0x11, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, - 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, - 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, - 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, - 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, - 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, - 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, - 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, - 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x40, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, - 0x69, 0x72, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xa4, 0x05, 0x0a, 0x15, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, 0x74, 0x61, - 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, - 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, - 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, - 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, - 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, - 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, - 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, - 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, - 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, - 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, - 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, - 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, - 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, - 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, - 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, - 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, - 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, - 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, - 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, - 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x22, - 0x85, 0x01, 0x0a, 0x1a, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x41, - 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x48, 0x00, 0x52, 0x0c, + 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x4d, 0x0a, 0x07, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xfc, 0x02, 0x0a, 0x14, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, - 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, - 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, - 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, - 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, + 0x48, 0x00, 0x52, 0x07, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x5b, 0x0a, 0x0f, 0x62, + 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, + 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, + 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x48, 0x00, 0x52, 0x0e, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x44, 0x0a, 0x04, 0x66, 0x75, 0x6c, 0x75, + 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x46, 0x75, 0x6c, 0x75, 0x48, 0x00, 0x52, 0x04, 0x66, 0x75, 0x6c, 0x75, 0x12, 0x52, + 0x0a, 0x0c, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x5f, 0x66, 0x75, 0x6c, 0x75, 0x18, 0x0c, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, + 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, + 0x75, 0x6c, 0x75, 0x48, 0x00, 0x52, 0x0b, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x46, 0x75, + 0x6c, 0x75, 0x12, 0x3f, 0x0a, 0x05, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x48, 0x00, 0x52, 0x05, 0x67, 0x6c, + 0x6f, 0x61, 0x73, 0x12, 0x58, 0x0a, 0x0e, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x5f, 0x63, 0x6f, 0x6e, + 0x74, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x47, 0x6c, 0x6f, 0x61, 0x73, 0x48, 0x00, 0x52, 0x0d, + 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1d, 0x0a, + 0x0a, 0x69, 0x73, 0x5f, 0x62, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x09, 0x69, 0x73, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x23, 0x0a, 0x0d, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x65, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x56, 0x61, 0x6c, 0x75, + 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, + 0x18, 0x66, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x55, + 0x72, 0x6c, 0x3a, 0x02, 0x18, 0x01, 0x42, 0x07, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x22, + 0x73, 0x0a, 0x11, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x38, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, + 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x22, 0xea, 0x02, 0x0a, 0x0b, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, - 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, - 0x74, 0x12, 0x43, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, - 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xfa, 0x05, 0x0a, 0x18, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, - 0x72, 0x69, 0x78, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, - 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, - 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, - 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, - 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, - 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, - 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, - 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, - 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, - 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, - 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x12, 0x51, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, - 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x22, 0x93, 0x01, 0x0a, 0x21, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x48, 0x0a, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x05, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x8a, 0x03, 0x0a, 0x1b, 0x42, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, - 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, - 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, - 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, - 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, - 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, - 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, - 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, - 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x4a, 0x0a, 0x04, 0x62, 0x6f, - 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, - 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x94, 0x06, 0x0a, 0x1f, 0x42, 0x6c, 0x69, 0x6e, 0x64, - 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, - 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, - 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, - 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, - 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, - 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, - 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, - 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, - 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, - 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, - 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, - 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, - 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, - 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, - 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x64, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, 0x81, 0x01, - 0x0a, 0x18, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x3f, 0x0a, 0x05, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, - 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x22, 0xf8, 0x02, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, + 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, + 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, - 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, - 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, - 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, - 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, - 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, - 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, - 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x41, 0x0a, 0x04, 0x62, 0x6f, 0x64, - 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x43, - 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xf3, 0x06, 0x0a, - 0x16, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, - 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, - 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, - 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, - 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, - 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, - 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, - 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, - 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, - 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, - 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, - 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, - 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, - 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, - 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, - 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, - 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x10, 0x65, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x72, - 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, - 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, - 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x1f, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, - 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, - 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x46, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, - 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, + 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, + 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, + 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, + 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x22, 0xd1, 0x04, 0x0a, 0x0f, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, + 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, + 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, + 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, + 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, + 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, + 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, + 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, + 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, + 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, + 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x69, 0x74, 0x73, 0x22, 0xb2, 0x01, 0x0a, 0x10, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x4e, 0x0a, 0x0d, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, + 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x12, 0x4e, 0x0a, 0x0d, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x65, + 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0x22, 0xad, 0x01, 0x0a, 0x12, 0x49, + 0x6e, 0x64, 0x65, 0x78, 0x65, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x12, 0x35, 0x0a, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x69, + 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x04, 0x42, 0x08, 0x92, 0xb5, + 0x18, 0x04, 0x32, 0x30, 0x34, 0x38, 0x52, 0x10, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, + 0x67, 0x49, 0x6e, 0x64, 0x69, 0x63, 0x65, 0x73, 0x12, 0x3a, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x04, + 0x64, 0x61, 0x74, 0x61, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, + 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x76, 0x0a, 0x1e, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x56, 0x31, 0x12, 0x50, 0x0a, 0x08, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x56, 0x31, 0x52, 0x08, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x3a, 0x02, + 0x18, 0x01, 0x22, 0x8f, 0x01, 0x0a, 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x56, 0x31, 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x56, 0x31, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x22, 0x86, 0x03, 0x0a, 0x19, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, - 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, - 0x6c, 0x61, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x74, 0x75, 0x72, 0x65, 0x22, 0xa1, 0x01, 0x0a, 0x17, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x31, + 0x12, 0x2b, 0x0a, 0x0d, 0x66, 0x65, 0x65, 0x5f, 0x72, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, + 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x30, 0x52, + 0x0c, 0x66, 0x65, 0x65, 0x52, 0x65, 0x63, 0x69, 0x70, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x0a, + 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, + 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, + 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x75, 0x0a, 0x10, 0x53, 0x69, 0x67, 0x6e, + 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x12, 0x3b, 0x0a, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, + 0x8e, 0x01, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x12, 0x42, + 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, + 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, + 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, + 0x22, 0x7f, 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x3e, 0x0a, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x6c, + 0x74, 0x61, 0x69, 0x72, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0xf6, 0x02, 0x0a, 0x11, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x41, 0x6c, 0x74, 0x61, 0x69, 0x72, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, + 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, + 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, + 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, + 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, + 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, + 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, + 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x40, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, + 0x74, 0x61, 0x69, 0x72, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xa4, 0x05, 0x0a, 0x15, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x41, 0x6c, + 0x74, 0x61, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, + 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, + 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, + 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, + 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, + 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, + 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, + 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, + 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, + 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, + 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, + 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, + 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, + 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, + 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, + 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, + 0x65, 0x22, 0x85, 0x01, 0x0a, 0x1a, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, + 0x12, 0x41, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x05, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xfc, 0x02, 0x0a, 0x14, 0x42, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, + 0x69, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, @@ -5171,120 +4939,241 @@ var file_proto_prysm_v1alpha1_beacon_block_proto_rawDesc = []byte{ 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x6f, 0x6f, 0x74, 0x12, 0x48, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, - 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, - 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, - 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x8d, 0x07, - 0x0a, 0x1d, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, - 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, - 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, - 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, - 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, + 0x6f, 0x6f, 0x74, 0x12, 0x43, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, + 0x69, 0x78, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xfa, 0x05, 0x0a, 0x18, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, + 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, + 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, + 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, + 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, + 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, + 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, + 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, + 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, + 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, + 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, + 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x12, 0x51, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x93, 0x01, 0x0a, 0x21, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x48, 0x0a, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, - 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, + 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x52, 0x05, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, + 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x8a, 0x03, 0x0a, 0x1b, + 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x58, 0x0a, 0x04, 0x73, + 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, + 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, + 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, + 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, + 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, + 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, + 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, + 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x4a, 0x0a, 0x04, + 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, - 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, - 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, - 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, - 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, - 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, - 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, + 0x69, 0x78, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0x94, 0x06, 0x0a, 0x1f, 0x42, 0x6c, 0x69, + 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, + 0x6f, 0x64, 0x79, 0x42, 0x65, 0x6c, 0x6c, 0x61, 0x74, 0x72, 0x69, 0x78, 0x12, 0x2b, 0x0a, 0x0d, + 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, + 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, + 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, + 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, - 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6b, 0x0a, 0x18, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, - 0x61, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x72, 0x0a, 0x18, 0x62, 0x6c, 0x73, - 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, - 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, + 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, + 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, + 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, + 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, + 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, + 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, + 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x64, 0x0a, 0x18, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x22, + 0x81, 0x01, 0x0a, 0x18, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x3f, 0x0a, 0x05, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, 0x53, 0x54, 0x6f, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, 0x6f, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, 0x83, 0x01, - 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, - 0x69, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x43, 0x61, 0x70, - 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, + 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, + 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, - 0x69, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x49, 0x0a, 0x06, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, - 0x65, 0x79, 0x22, 0xc2, 0x01, 0x0a, 0x1e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, - 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x43, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, - 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, - 0x6e, 0x65, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, - 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, - 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, - 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, - 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, - 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, - 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x7d, 0x0a, 0x16, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, - 0x62, 0x12, 0x3d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x75, 0x72, 0x65, 0x22, 0xf8, 0x02, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, + 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, + 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, + 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, + 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, + 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, + 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, + 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x41, 0x0a, 0x04, 0x62, + 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, + 0x79, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xf3, + 0x06, 0x0a, 0x16, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, + 0x64, 0x79, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, + 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, + 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, + 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, + 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, + 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, + 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, + 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, + 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, + 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, + 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, + 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, + 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, + 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, + 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, + 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, + 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, + 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x58, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x10, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x72, 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x42, 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, + 0x6c, 0x73, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x22, 0x8f, 0x01, 0x0a, 0x1f, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x46, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x18, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x44, 0x65, - 0x6e, 0x65, 0x62, 0x12, 0x3d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, - 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, - 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, - 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, - 0xf4, 0x02, 0x0a, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, - 0x65, 0x6e, 0x65, 0x62, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x86, 0x03, 0x0a, 0x19, 0x42, 0x6c, 0x69, 0x6e, 0x64, + 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x61, 0x70, + 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, @@ -5302,152 +5191,60 @@ var file_proto_prysm_v1alpha1_beacon_block_proto_rawDesc = []byte{ 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, 0x6e, 0x65, 0x62, - 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xb3, 0x07, 0x0a, 0x14, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, - 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, - 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, - 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, - 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, - 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, - 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, - 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, - 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, - 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, - 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, - 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, - 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, - 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, - 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, - 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x11, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, - 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, - 0x65, 0x62, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x72, 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, - 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, - 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, - 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, - 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, - 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, - 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8f, 0x01, 0x0a, - 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x48, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, - 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x82, - 0x03, 0x0a, 0x17, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, - 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, - 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, - 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, - 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, - 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, - 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, - 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, - 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, - 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, - 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x46, 0x0a, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x48, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, + 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, + 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, + 0x64, 0x79, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, + 0x8d, 0x07, 0x0a, 0x1d, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, + 0x61, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, + 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, + 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, + 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, + 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, + 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, + 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, + 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, + 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, + 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, - 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x04, 0x62, - 0x6f, 0x64, 0x79, 0x22, 0xcd, 0x07, 0x0a, 0x1b, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, - 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, - 0x6e, 0x65, 0x62, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, - 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, - 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, - 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, - 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, - 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, - 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, - 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, - 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, - 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, - 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, - 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, - 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, - 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, - 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, - 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, - 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, - 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, - 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, - 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, - 0x12, 0x69, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, - 0x6e, 0x65, 0x62, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, + 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x6b, + 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, + 0x6c, 0x6c, 0x61, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x72, 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, @@ -5455,219 +5252,343 @@ var file_proto_prysm_v1alpha1_beacon_block_proto_rawDesc = []byte{ 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, 0x6f, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, - 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, - 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, - 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x22, 0x7f, 0x0a, 0x15, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x75, 0x69, - 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x40, 0x0a, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, - 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, - 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, - 0x74, 0x75, 0x72, 0x65, 0x22, 0xdc, 0x01, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, - 0x42, 0x69, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x47, 0x0a, 0x06, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, - 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, - 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, - 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, - 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, - 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, - 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, - 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, - 0x6b, 0x65, 0x79, 0x22, 0xb4, 0x02, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, - 0x69, 0x64, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x47, 0x0a, 0x06, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, - 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, - 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, - 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, - 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x83, 0x01, 0x0a, 0x17, 0x53, - 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x45, - 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, - 0x61, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, - 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, - 0x22, 0x55, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x73, - 0x12, 0x45, 0x0a, 0x08, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x6f, 0x62, 0x53, - 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x36, 0x52, 0x08, 0x73, - 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x73, 0x22, 0xc0, 0x02, 0x0a, 0x0b, 0x42, 0x6c, 0x6f, 0x62, - 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x1e, 0x0a, - 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0x8a, 0xb5, 0x18, - 0x06, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x12, 0x2d, 0x0a, - 0x0e, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x0d, 0x6b, - 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x0a, 0x09, - 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, - 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x08, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, - 0x66, 0x12, 0x5e, 0x0a, 0x13, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2e, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x11, - 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x12, 0x47, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x5f, - 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x09, 0x8a, 0xb5, 0x18, 0x05, 0x31, 0x37, 0x2c, 0x33, 0x32, - 0x52, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x63, 0x6c, - 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0xc6, 0x01, 0x0a, 0x20, 0x53, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x22, + 0x83, 0x01, 0x0a, 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x42, 0x69, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x42, 0x0a, 0x07, 0x6d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x43, + 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x9c, 0x01, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x42, 0x69, 0x64, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x12, 0x49, 0x0a, 0x06, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x43, 0x61, 0x70, 0x65, 0x6c, 0x6c, 0x61, 0x52, 0x06, + 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, + 0x62, 0x6b, 0x65, 0x79, 0x22, 0xc2, 0x01, 0x0a, 0x1e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, + 0x74, 0x73, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x43, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, - 0x45, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, - 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, - 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, - 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x09, 0x6b, 0x7a, - 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, - 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, - 0x6f, 0x62, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x18, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, - 0x12, 0x3f, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, - 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x3f, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, + 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, + 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, + 0x39, 0x36, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, + 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, + 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, + 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x7d, 0x0a, 0x16, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, + 0x6e, 0x65, 0x62, 0x12, 0x3d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x05, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xb6, 0x01, 0x0a, 0x18, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, + 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x3d, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, + 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, + 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, + 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, + 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, + 0x73, 0x22, 0xf4, 0x02, 0x0a, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, + 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, + 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, + 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, + 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, + 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, + 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x3f, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, - 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, - 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, - 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x09, 0x6b, - 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, - 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, - 0x6c, 0x6f, 0x62, 0x73, 0x22, 0xf8, 0x02, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x58, 0x0a, 0x04, 0x73, - 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, - 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, - 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, - 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, - 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, - 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, - 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, 0x70, - 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, 0x0b, - 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, - 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, - 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, - 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x41, 0x0a, 0x04, - 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, - 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, - 0x97, 0x08, 0x0a, 0x16, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, - 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, - 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, - 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, - 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, - 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, - 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x64, 0x0a, 0x12, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x45, 0x6c, - 0x65, 0x63, 0x74, 0x72, 0x61, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x11, 0x61, 0x74, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, 0x6e, + 0x65, 0x62, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xb3, 0x07, 0x0a, 0x14, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, 0x6e, 0x65, + 0x62, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, + 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, + 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, + 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, + 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, + 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, + 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, + 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, + 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, - 0x54, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, + 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, - 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, - 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, - 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, - 0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x72, 0x0a, 0x18, 0x62, - 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, + 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, + 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, + 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, + 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, 0x53, 0x54, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, + 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, + 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, + 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x56, + 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, + 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, + 0x65, 0x6e, 0x65, 0x62, 0x52, 0x10, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x72, 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, + 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, + 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, + 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, + 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, + 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, + 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, + 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, + 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x8f, + 0x01, 0x0a, 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, + 0x12, 0x48, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, + 0x62, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x22, 0x82, 0x03, 0x0a, 0x17, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, + 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x58, 0x0a, 0x04, + 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, + 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, + 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, + 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, + 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, + 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, + 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, + 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, + 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, + 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, + 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x46, 0x0a, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, + 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xcd, 0x07, 0x0a, 0x1b, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, + 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, + 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, + 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, + 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, + 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, + 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, + 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, + 0x69, 0x6e, 0x67, 0x73, 0x12, 0x5d, 0x0a, 0x12, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x32, + 0x52, 0x11, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, + 0x6e, 0x67, 0x73, 0x12, 0x4f, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x92, + 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, + 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, + 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x08, + 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, 0x75, + 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, 0x92, + 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, + 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, 0x67, + 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, + 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, + 0x74, 0x65, 0x12, 0x69, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x0a, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x72, 0x0a, + 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, + 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, 0x6f, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x12, - 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, - 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, + 0x73, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, 0x42, + 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, + 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x7f, 0x0a, 0x15, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x40, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x26, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, + 0x69, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, + 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xdc, 0x01, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x42, 0x69, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x12, 0x47, 0x0a, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, + 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, + 0x30, 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, + 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0xb4, 0x02, 0x0a, 0x11, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x42, 0x69, 0x64, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x47, 0x0a, 0x06, 0x68, + 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, + 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, + 0x34, 0x30, 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x1c, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1e, 0x0a, 0x06, + 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, 0x83, 0x01, 0x0a, + 0x17, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, + 0x64, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x42, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x69, 0x64, 0x45, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x61, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, + 0x72, 0x65, 0x22, 0x55, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x62, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, + 0x72, 0x73, 0x12, 0x45, 0x0a, 0x08, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x6f, + 0x62, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x36, 0x52, + 0x08, 0x73, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x73, 0x22, 0xc0, 0x02, 0x0a, 0x0b, 0x42, 0x6c, + 0x6f, 0x62, 0x53, 0x69, 0x64, 0x65, 0x63, 0x61, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, + 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x1e, 0x0a, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x0a, 0x8a, + 0xb5, 0x18, 0x06, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x52, 0x04, 0x62, 0x6c, 0x6f, 0x62, 0x12, + 0x2d, 0x0a, 0x0e, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, + 0x0d, 0x6b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x23, + 0x0a, 0x09, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x08, 0x6b, 0x7a, 0x67, 0x50, 0x72, + 0x6f, 0x6f, 0x66, 0x12, 0x5e, 0x0a, 0x13, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x52, 0x11, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x12, 0x47, 0x0a, 0x1a, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x09, 0x8a, 0xb5, 0x18, 0x05, 0x31, 0x37, 0x2c, + 0x33, 0x32, 0x52, 0x18, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x6e, + 0x63, 0x6c, 0x75, 0x73, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x22, 0xc6, 0x01, 0x0a, + 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, + 0x61, 0x12, 0x45, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, + 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, + 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, + 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x09, + 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, + 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, + 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, + 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x18, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x12, 0x3f, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, + 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xba, 0x01, 0x0a, 0x1a, 0x42, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, + 0x73, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x3f, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2f, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, + 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, - 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, - 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x1f, 0x53, 0x69, - 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x4a, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x30, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, - 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, - 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, - 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, - 0x86, 0x03, 0x0a, 0x19, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, + 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, + 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, + 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, + 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0xf8, 0x02, 0x0a, 0x12, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, @@ -5686,155 +5607,254 @@ var file_proto_prysm_v1alpha1_beacon_block_proto_rawDesc = []byte{ 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, - 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x48, - 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x65, + 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x41, + 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, - 0x72, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xb1, 0x08, 0x0a, 0x1d, 0x42, 0x6c, 0x69, - 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, - 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x2b, 0x0a, 0x0d, 0x72, 0x61, - 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, 0x64, 0x61, - 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, 0x31, 0x5f, - 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, 0x74, 0x68, - 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, - 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, 0x74, 0x68, - 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, - 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, - 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, 0x72, 0x6f, - 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, - 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x64, 0x0a, 0x12, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x45, 0x6c, - 0x65, 0x63, 0x74, 0x72, 0x61, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x11, 0x61, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, - 0x54, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, - 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, + 0x79, 0x22, 0x97, 0x08, 0x0a, 0x16, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x2b, 0x0a, 0x0d, + 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, + 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, + 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, + 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, - 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, - 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, 0x6f, 0x6c, - 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, - 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, - 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x42, 0x06, - 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, - 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x5f, 0x61, - 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x64, 0x0a, 0x12, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x11, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x54, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, + 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, + 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, + 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x56, 0x0a, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, + 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x10, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x72, 0x0a, + 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x4c, + 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, + 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, 0x54, + 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, + 0x73, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, 0x42, + 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, + 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, + 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, + 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0x93, 0x01, 0x0a, 0x1f, + 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, + 0x4a, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x30, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, + 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, + 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, + 0x65, 0x22, 0x86, 0x03, 0x0a, 0x19, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, + 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, + 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, + 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, + 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, + 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, + 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, + 0x78, 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x12, 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, + 0x61, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, + 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, + 0x12, 0x48, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, - 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, 0x65, 0x67, - 0x61, 0x74, 0x65, 0x12, 0x69, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, - 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x72, - 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, - 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, - 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, - 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, - 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, 0x6c, 0x73, - 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x73, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, - 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0c, - 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, - 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, - 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0d, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, - 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0xc4, 0x01, 0x0a, - 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x75, 0x12, 0x42, - 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, + 0x63, 0x74, 0x72, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x22, 0xb1, 0x08, 0x0a, 0x1d, 0x42, + 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x12, 0x2b, 0x0a, 0x0d, + 0x72, 0x61, 0x6e, 0x64, 0x61, 0x6f, 0x5f, 0x72, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x0c, 0x72, 0x61, 0x6e, + 0x64, 0x61, 0x6f, 0x52, 0x65, 0x76, 0x65, 0x61, 0x6c, 0x12, 0x3c, 0x0a, 0x09, 0x65, 0x74, 0x68, + 0x31, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x65, + 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, + 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x52, 0x08, 0x65, + 0x74, 0x68, 0x31, 0x44, 0x61, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, + 0x69, 0x74, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, + 0x32, 0x52, 0x08, 0x67, 0x72, 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x12, 0x5e, 0x0a, 0x12, 0x70, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, + 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x64, 0x0a, 0x12, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x31, 0x52, 0x11, + 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x73, 0x12, 0x54, 0x0a, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, + 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6c, 0x65, 0x63, 0x74, + 0x72, 0x61, 0x42, 0x05, 0x92, 0xb5, 0x18, 0x01, 0x38, 0x52, 0x0c, 0x61, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x65, 0x74, 0x68, 0x65, + 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, + 0x31, 0x2e, 0x44, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, + 0x36, 0x52, 0x08, 0x64, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x73, 0x12, 0x5b, 0x0a, 0x0f, 0x76, + 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x73, 0x18, 0x08, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, + 0x6e, 0x65, 0x64, 0x56, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, + 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x0e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, + 0x61, 0x72, 0x79, 0x45, 0x78, 0x69, 0x74, 0x73, 0x12, 0x4b, 0x0a, 0x0e, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x61, 0x67, 0x67, 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, + 0x72, 0x65, 0x67, 0x61, 0x74, 0x65, 0x52, 0x0d, 0x73, 0x79, 0x6e, 0x63, 0x41, 0x67, 0x67, 0x72, + 0x65, 0x67, 0x61, 0x74, 0x65, 0x12, 0x69, 0x0a, 0x18, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, + 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x16, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x12, 0x72, 0x0a, 0x18, 0x62, 0x6c, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x73, 0x18, 0x0b, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, + 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, + 0x64, 0x42, 0x4c, 0x53, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x06, 0x92, 0xb5, 0x18, 0x02, 0x31, 0x36, 0x52, 0x15, 0x62, + 0x6c, 0x73, 0x54, 0x6f, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x73, 0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, + 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x03, + 0x28, 0x0c, 0x42, 0x10, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, + 0x34, 0x30, 0x39, 0x36, 0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, + 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x0d, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, + 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, + 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x22, 0xc4, + 0x01, 0x0a, 0x1d, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x75, + 0x12, 0x42, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, + 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x75, 0x52, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x33, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, + 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, + 0x34, 0x38, 0x92, 0xb5, 0x18, 0x08, 0x33, 0x33, 0x35, 0x35, 0x34, 0x34, 0x33, 0x32, 0x52, 0x09, + 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, + 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, + 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, + 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x22, 0x7e, 0x0a, 0x15, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x75, 0x12, 0x3f, + 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, - 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x75, 0x52, 0x05, 0x62, 0x6c, 0x6f, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, + 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x17, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, + 0x75, 0x12, 0x3f, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, + 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x33, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x08, 0x33, 0x33, 0x35, 0x35, 0x34, 0x34, 0x33, 0x32, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, - 0x6f, 0x62, 0x73, 0x22, 0x7e, 0x0a, 0x15, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x75, 0x12, 0x3f, 0x0a, 0x05, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x45, - 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, + 0x6f, 0x62, 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x46, 0x75, 0x6c, 0x75, 0x12, 0x47, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, + 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, + 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x46, 0x75, 0x6c, 0x75, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, - 0x75, 0x72, 0x65, 0x22, 0xbb, 0x01, 0x0a, 0x17, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x46, 0x75, 0x6c, 0x75, 0x12, - 0x3f, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, - 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, - 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, 0x61, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x12, 0x33, 0x0a, 0x0a, 0x6b, 0x7a, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, - 0x18, 0x08, 0x33, 0x33, 0x35, 0x35, 0x34, 0x34, 0x33, 0x32, 0x52, 0x09, 0x6b, 0x7a, 0x67, 0x50, - 0x72, 0x6f, 0x6f, 0x66, 0x73, 0x12, 0x2a, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x03, - 0x20, 0x03, 0x28, 0x0c, 0x42, 0x14, 0x8a, 0xb5, 0x18, 0x08, 0x3f, 0x2c, 0x31, 0x33, 0x31, 0x30, - 0x37, 0x32, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x62, - 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x1c, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x42, 0x6c, 0x69, 0x6e, - 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, - 0x6c, 0x75, 0x12, 0x47, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, - 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, - 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, - 0x6c, 0x75, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, - 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, - 0x8a, 0xb5, 0x18, 0x02, 0x39, 0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, - 0x65, 0x22, 0x83, 0x03, 0x0a, 0x16, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, - 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x75, 0x12, 0x58, 0x0a, 0x04, - 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, - 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, - 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, - 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, - 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, - 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0d, - 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x27, 0x0a, - 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, 0x72, 0x65, - 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, - 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, - 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x48, 0x0a, - 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x65, 0x74, - 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, 0x63, 0x6f, - 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, 0x74, 0x72, - 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x9a, 0x01, 0x0a, 0x19, 0x6f, 0x72, 0x67, 0x2e, + 0x75, 0x72, 0x65, 0x22, 0x83, 0x03, 0x0a, 0x16, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, + 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x75, 0x6c, 0x75, 0x12, 0x58, + 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, + 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, + 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, + 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x12, 0x75, 0x0a, 0x0e, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, + 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, + 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, + 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x52, 0x0d, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x12, + 0x27, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0a, 0x70, 0x61, + 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, + 0x18, 0x02, 0x33, 0x32, 0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x48, 0x0a, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, - 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, - 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, - 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x45, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x15, 0x45, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, 0x61, 0x6c, - 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x6c, 0x69, 0x6e, 0x64, 0x65, 0x64, 0x42, 0x65, 0x61, + 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x42, 0x6f, 0x64, 0x79, 0x45, 0x6c, 0x65, 0x63, + 0x74, 0x72, 0x61, 0x52, 0x04, 0x62, 0x6f, 0x64, 0x79, 0x42, 0x9a, 0x01, 0x0a, 0x19, 0x6f, 0x72, + 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, + 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x10, 0x42, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x39, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, + 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0xaa, 0x02, 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, + 0x6d, 0x2e, 0x45, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, + 0x15, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x45, 0x74, 0x68, 0x5c, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/prysm/v1alpha1/beacon_block.proto b/proto/prysm/v1alpha1/beacon_block.proto index 1ff3da598814..75ba22246a48 100644 --- a/proto/prysm/v1alpha1/beacon_block.proto +++ b/proto/prysm/v1alpha1/beacon_block.proto @@ -78,6 +78,9 @@ message GenericSignedBeaconBlock { } bool is_blinded = 100; reserved 101; // Deprecated fields + // Winning builder to forward the signed block to; set from the produce + // response (gRPC field, REST Eth-Builder-Url header). + string builder_url = 102; } message GenericBeaconBlock { @@ -128,6 +131,9 @@ message GenericBeaconBlock { } bool is_blinded = 100; string payload_value = 101; + // Winning Builder-API builder, echoed at publish so any beacon node can + // forward the signed block to it. Empty for self-build and P2P bids. + string builder_url = 102; } // ---------------------------------------------------------------------------- diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index 2d8cdc79fb14..9bf0fb8b6125 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -42,20 +42,16 @@ message BuilderPreferencesRequestV1 { SignedRequestAuthV1 auth = 2; } -// SubmitBuilderPreferencesRequest asks the beacon node to forward a proposer's -// signed preferences to one builder ahead of a proposal slot (the builder-specs -// preferences channel). url identifies the builder explicitly: per builder-specs -// 5078eab the signed auth data is opaque and no longer carries the URL. +// SubmitBuilderPreferencesRequest forwards a proposer's signed preferences to +// one builder ahead of a proposal slot; url is explicit, the auth data is opaque. message SubmitBuilderPreferencesRequest { bytes validator_pubkey = 1; string url = 2; BuilderPreferencesRequestV1 request = 3; } -// BuilderPreferenceV1 is one builder's preferences for a single block proposal, -// carried inline on the block request (JIT model, beacon-APIs #625). request is -// the signed preferences plus auth the beacon node forwards to the builder; -// min_bid and builder_boost_factor are proposer-local and only gate bid selection. +// BuilderPreferenceV1 is one builder's preferences for a single proposal, carried +// inline on the block request. min_bid and builder_boost_factor never leave the node. message BuilderPreferenceV1 { BuilderPreferencesRequestV1 request = 1; optional uint64 min_bid = 2 [ @@ -63,7 +59,6 @@ message BuilderPreferenceV1 { "github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei" ]; optional uint64 builder_boost_factor = 3; - // url identifies the builder. Per builder-specs 5078eab the signed request auth - // data is opaque, so the URL is no longer recoverable from it and is carried here. + // The signed auth data is opaque, so the builder URL is carried explicitly. string url = 4; } diff --git a/validator/client/beacon-api/beacon_api_validator_client.go b/validator/client/beacon-api/beacon_api_validator_client.go index b0a785284307..d4a37944d6a7 100644 --- a/validator/client/beacon-api/beacon_api_validator_client.go +++ b/validator/client/beacon-api/beacon_api_validator_client.go @@ -300,9 +300,14 @@ func (c *beaconApiValidatorClient) SubmitSignedProposerPreferences(ctx context.C }) } -// TODO(gloas): no beacon-API endpoint exists for the builder preferences forward yet. -func (c *beaconApiValidatorClient) SubmitBuilderPreferences(_ context.Context, _ *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { - log.Debug("SubmitBuilderPreferences not yet implemented for beacon API client, skipping") +// SubmitBuilderPreferences pushes signed builder preferences ahead of the +// proposal slot (beacon-APIs #630). +func (c *beaconApiValidatorClient) SubmitBuilderPreferences(ctx context.Context, in *ethpb.SubmitBuilderPreferencesRequest) (*empty.Empty, error) { + ctx, span := trace.StartSpan(ctx, "beacon-api.SubmitBuilderPreferences") + defer span.End() + if err := c.submitBuilderPreferences(ctx, in); err != nil { + return nil, err + } return new(empty.Empty), nil } diff --git a/validator/client/beacon-api/builder_preferences.go b/validator/client/beacon-api/builder_preferences.go index 5b2114d509c2..96dc8fb09fd8 100644 --- a/validator/client/beacon-api/builder_preferences.go +++ b/validator/client/beacon-api/builder_preferences.go @@ -1,27 +1,35 @@ package beacon_api import ( + "bytes" + "context" "encoding/json" "strconv" ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/pkg/errors" ) -// JIT builder-preferences POST body for produceBlockV4 (beacon-APIs #625). -type builderPreferencesBody struct { - BuilderPreferences []*builderPreferenceReq `json:"builder_preferences"` +// builderEntryReq is one element of the produceBlockV4 POST body (beacon-APIs +// #630): a bare array of per-builder entries. +type builderEntryReq struct { + Url string `json:"url"` + Auth *signedAuthReq `json:"auth"` + MaxExecutionPayment string `json:"max_execution_payment,omitempty"` + MinBid string `json:"min_bid,omitempty"` + BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` } -type builderPreferenceReq struct { - Url string `json:"url"` - SignedRequestAuth *signedRequestAuthReq `json:"signed_request_auth"` - MaxExecutionPayment string `json:"max_execution_payment"` - MinBid string `json:"min_bid,omitempty"` - BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` +// builderPreferenceEntryReq is one element of the ahead-of-time preferences POST +// body for /eth/v1/validator/builder_preferences/{pubkey}. +type builderPreferenceEntryReq struct { + Url string `json:"url"` + Auth *signedAuthReq `json:"auth"` + MaxExecutionPayment string `json:"max_execution_payment"` } -type signedRequestAuthReq struct { +type signedAuthReq struct { Message *requestAuthReq `json:"message"` Signature string `json:"signature"` } @@ -31,31 +39,53 @@ type requestAuthReq struct { Slot string `json:"slot"` } -// marshalBuilderPreferences encodes inline builder preferences into the #625 JSON body. +func signedAuthFromConsensus(auth *ethpb.SignedRequestAuthV1) *signedAuthReq { + return &signedAuthReq{ + Message: &requestAuthReq{ + Data: hexutil.Encode(auth.Message.Data), + Slot: strconv.FormatUint(uint64(auth.Message.Slot), 10), + }, + Signature: hexutil.Encode(auth.Signature), + } +} + +// marshalBuilderPreferences encodes inline builder preferences into the #630 +// produce-block body: a bare array of builder entries. func marshalBuilderPreferences(prefs []*ethpb.BuilderPreferenceV1) ([]byte, error) { - body := &builderPreferencesBody{BuilderPreferences: make([]*builderPreferenceReq, 0, len(prefs))} + entries := make([]*builderEntryReq, 0, len(prefs)) for _, p := range prefs { if p == nil || p.Request == nil || p.Request.Auth == nil || p.Request.Auth.Message == nil { continue } - req := &builderPreferenceReq{ + entry := &builderEntryReq{ Url: p.Url, + Auth: signedAuthFromConsensus(p.Request.Auth), MaxExecutionPayment: strconv.FormatUint(uint64(p.Request.Preferences.GetMaxExecutionPayment()), 10), - SignedRequestAuth: &signedRequestAuthReq{ - Message: &requestAuthReq{ - Data: hexutil.Encode(p.Request.Auth.Message.Data), - Slot: strconv.FormatUint(uint64(p.Request.Auth.Message.Slot), 10), - }, - Signature: hexutil.Encode(p.Request.Auth.Signature), - }, } if p.MinBid != nil { - req.MinBid = strconv.FormatUint(uint64(p.GetMinBid()), 10) + entry.MinBid = strconv.FormatUint(uint64(p.GetMinBid()), 10) } if p.BuilderBoostFactor != nil { - req.BuilderBoostFactor = strconv.FormatUint(p.GetBuilderBoostFactor(), 10) + entry.BuilderBoostFactor = strconv.FormatUint(p.GetBuilderBoostFactor(), 10) } - body.BuilderPreferences = append(body.BuilderPreferences, req) + entries = append(entries, entry) + } + return json.Marshal(entries) +} + +// submitBuilderPreferences pushes one signed builder preference ahead of the +// proposal slot via POST /eth/v1/validator/builder_preferences/{pubkey}. +func (c *beaconApiValidatorClient) submitBuilderPreferences(ctx context.Context, in *ethpb.SubmitBuilderPreferencesRequest) error { + if in == nil || in.Request == nil || in.Request.Auth == nil || in.Request.Auth.Message == nil { + return errors.New("builder preferences request is empty") + } + body, err := json.Marshal([]*builderPreferenceEntryReq{{ + Url: in.Url, + Auth: signedAuthFromConsensus(in.Request.Auth), + MaxExecutionPayment: strconv.FormatUint(uint64(in.Request.Preferences.GetMaxExecutionPayment()), 10), + }}) + if err != nil { + return errors.Wrap(err, "could not marshal builder preference entry") } - return json.Marshal(body) + return c.handler.Post(ctx, "/eth/v1/validator/builder_preferences/"+hexutil.Encode(in.ValidatorPubkey), nil, bytes.NewBuffer(body), nil) } diff --git a/validator/client/beacon-api/get_beacon_block.go b/validator/client/beacon-api/get_beacon_block.go index fe10ece098ef..794647ee80e4 100644 --- a/validator/client/beacon-api/get_beacon_block.go +++ b/validator/client/beacon-api/get_beacon_block.go @@ -89,6 +89,7 @@ func (c *beaconApiValidatorClient) beaconBlockV4(ctx context.Context, slot primi return nil, errors.Wrap(err, "could not get v4 beacon block") } + builderURL := header.Get(api.EthBuilderUrlHeader) payloadIncluded := header.Get(api.ExecutionPayloadIncludedHeader) == "true" isSSZ := strings.Contains(header.Get("Content-Type"), api.OctetStreamMediaType) @@ -109,13 +110,13 @@ func (c *beaconApiValidatorClient) beaconBlockV4(ctx context.Context, slot primi if c.stateless && contents.ExecutionPayloadEnvelope != nil { c.envelopeCache.Add(slot, contents.ExecutionPayloadEnvelope, contents.Blobs, contents.KzgProofs) } - return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Gloas{Gloas: contents.Block}}, nil + return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Gloas{Gloas: contents.Block}, BuilderUrl: builderURL}, nil } block := ðpb.BeaconBlockGloas{} if err := block.UnmarshalSSZ(data); err != nil { return nil, errors.Wrap(err, "failed to unmarshal gloas block SSZ") } - return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Gloas{Gloas: block}}, nil + return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Gloas{Gloas: block}, BuilderUrl: builderURL}, nil } // JSON, payload not included: parse the bare block. @@ -131,6 +132,7 @@ func (c *beaconApiValidatorClient) beaconBlockV4(ctx context.Context, slot primi if err != nil { return nil, errors.Wrap(err, "could not convert gloas block to generic") } + blk.BuilderUrl = builderURL return blk, nil } diff --git a/validator/client/beacon-api/propose_beacon_block.go b/validator/client/beacon-api/propose_beacon_block.go index e305211d280c..cbbfb8823d55 100644 --- a/validator/client/beacon-api/propose_beacon_block.go +++ b/validator/client/beacon-api/propose_beacon_block.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "github.com/OffchainLabs/prysm/v7/api" "net/http" "github.com/OffchainLabs/prysm/v7/api/server/structs" @@ -167,6 +168,9 @@ func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *e } headers := map[string]string{"Eth-Consensus-Version": res.consensusVersion} + if in.BuilderUrl != "" { + headers[api.EthBuilderUrlHeader] = in.BuilderUrl + } // Try PostSSZ first with SSZ data if res.marshalledSSZ != nil { diff --git a/validator/client/log_test.go b/validator/client/log_test.go index 3eecab1785c6..ef83f0a90b9a 100644 --- a/validator/client/log_test.go +++ b/validator/client/log_test.go @@ -96,7 +96,7 @@ func TestFromAttData(t *testing.T) { require.NoError(t, key2.FromAttData(att2.Data)) assert.NotEqual(t, key, key2, "distinct att data must produce distinct keys") } - + func TestLogSubmittedSyncCommitteeMessages(t *testing.T) { logHook := logTest.NewGlobal() v := validator{} diff --git a/validator/client/propose.go b/validator/client/propose.go index 0e06dc058693..179e22e4c01d 100644 --- a/validator/client/propose.go +++ b/validator/client/propose.go @@ -175,6 +175,9 @@ func (v *validator) ProposeBlock(ctx context.Context, slot primitives.Slot, pubK } } + // Echo the winning builder from the produce response. + genericSignedBlock.BuilderUrl = b.BuilderUrl + blkResp, err := v.validatorClient.ProposeBeaconBlock(ctx, genericSignedBlock) if err != nil { log.WithField("slot", slot).WithError(err).Error("Failed to propose block") diff --git a/validator/client/request_auth.go b/validator/client/request_auth.go index 54961f69a702..c2993bcd5b3b 100644 --- a/validator/client/request_auth.go +++ b/validator/client/request_auth.go @@ -34,8 +34,7 @@ func (v *validator) pruneSignedRequestAuths(slot primitives.Slot) { } // signRequestAuthCached signs a request auth for the (pk, relay, authData, slot) -// tuple. Per builder-specs 5078eab the signed data is the opaque authData agreed -// with the builder out of band, not the builder URL. +// tuple; the signed data is the opaque authData, not the builder URL. func (v *validator) signRequestAuthCached(ctx context.Context, km keymanager.IKeymanager, pk pubkey, relay string, authData []byte, slot primitives.Slot) (*ethpb.SignedRequestAuthV1, error) { key := requestAuthKey{pk: pk, slot: slot, relay: relay, authData: string(authData)} v.signedRequestAuthsLock.Lock() diff --git a/validator/client/validator.go b/validator/client/validator.go index d93d88c404be..80358573252c 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1395,7 +1395,7 @@ func (v *validator) builderTargetsForKey(pk pubkey) []builderTarget { if !bc.IsEnabled() { return nil } - // Unset max payment resolves to 0 (trustless-only), the safe client default. + // Unset resolves to 0, trustless-only. var fbMax uint64 if bc.MaxExecutionPayment != nil { fbMax = uint64(*bc.MaxExecutionPayment) @@ -1434,11 +1434,8 @@ func uint64Ptr(v *validatortypes.Uint64) *uint64 { return &u } -// warmBuilderRequestAuths pre-signs the builder request auths for upcoming -// proposal slots so proposeBlock can attach them inline (JIT) without signing on -// the hot path, and returns the ahead-of-time submissions for the builder-specs -// preferences channel. Rebuilt every push so builders learn preferences ahead of -// the slot even across beacon node restarts. +// warmBuilderRequestAuths pre-signs request auths for upcoming proposal slots and +// returns the ahead-of-time preference submissions, rebuilt every push. func (v *validator) warmBuilderRequestAuths(ctx context.Context, km keymanager.IKeymanager, slot primitives.Slot) []*ethpb.SubmitBuilderPreferencesRequest { if slots.ToEpoch(slot)+1 < params.BeaconConfig().GloasForkEpoch { return nil diff --git a/validator/rpc/handlers_keymanager.go b/validator/rpc/handlers_keymanager.go index 3c989a73ba29..1d2c245a0f6c 100644 --- a/validator/rpc/handlers_keymanager.go +++ b/validator/rpc/handlers_keymanager.go @@ -868,8 +868,7 @@ func (s *Server) SetGraffiti(w http.ResponseWriter, r *http.Request) { return } - // Graffiti mutates proposer settings via a read-modify-write in the - // validator client, so it must serialize with the other settings writers. + // Graffiti writes proposer settings, so it serializes with the other writers. s.proposerSettingsLock.Lock() defer s.proposerSettingsLock.Unlock() if err := s.validatorService.SetGraffiti(ctx, bytesutil.ToBytes48(pubkey), []byte(req.Graffiti)); err != nil { diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index e3ad6e1492b6..cfd1290de935 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -72,7 +72,7 @@ func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { return } - // Unrecognized fields are ignored for forward compatibility, per the spec. + // Unrecognized fields are ignored for forward compatibility. var req SetValidatorConfigRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) @@ -105,8 +105,7 @@ func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusError, Message: err.Error()} continue } - // Keys the server does not yet manage are stored anyway (pre-provisioning - // per the spec); the config takes effect when the key is later added. + // Unknown keys are stored too; the config applies once the key is added. if cfg == nil || cfg.isEmpty() { delete(settings.ProposeConfig, pubkey) resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusSet} From f7c4e1245b5420100db4b3b6f5fdb94fc79021b7 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 10:59:02 -0500 Subject: [PATCH 08/19] handle duplicate urls correctly --- beacon-chain/rpc/eth/validator/builder_preferences.go | 6 ++++++ .../rpc/eth/validator/builder_preferences_test.go | 8 ++++++++ validator/client/request_auth_test.go | 8 ++++---- validator/client/validator.go | 6 ++++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index a5e7795ff756..ac96cde38de0 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -55,12 +55,18 @@ func parseBuilderPreferencesBody(r *http.Request) ([]*eth.BuilderPreferenceV1, e return nil, errors.Wrap(err, "could not decode builder preferences") } out := make([]*eth.BuilderPreferenceV1, 0, len(entries)) + seen := make(map[string]bool, len(entries)) for _, p := range entries { conv, err := p.toConsensus() if err != nil { log.WithError(err).Debug("Ignoring malformed builder entry") continue } + // Duplicate urls invalidate the whole request, unlike malformed entries. + if seen[conv.Url] { + return nil, errors.Errorf("two builder entries share the same url") + } + seen[conv.Url] = true out = append(out, conv) } return out, nil diff --git a/beacon-chain/rpc/eth/validator/builder_preferences_test.go b/beacon-chain/rpc/eth/validator/builder_preferences_test.go index 2065d4dc3cbb..d572a3df870f 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences_test.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences_test.go @@ -53,3 +53,11 @@ func repeatHex(n int) string { } return string(out) } + +// Two entries sharing a url invalidate the whole request, unlike malformed entries. +func TestParseBuilderPreferencesBody_DuplicateURL(t *testing.T) { + entry := `{"url":"https://b","auth":{"message":{"data":"0x01","slot":"7"},"signature":"0x` + repeatHex(96) + `"}}` + r := httptest.NewRequest("POST", "/", bytes.NewBufferString(`[`+entry+`,`+entry+`]`)) + _, err := parseBuilderPreferencesBody(r) + require.ErrorContains(t, "share the same url", err) +} diff --git a/validator/client/request_auth_test.go b/validator/client/request_auth_test.go index 8dc81d0b0116..52c0ec5e5872 100644 --- a/validator/client/request_auth_test.go +++ b/validator/client/request_auth_test.go @@ -47,8 +47,9 @@ func TestBuildBuilderPreferencesForSlot_StaleAuthNotServedAfterRotation(t *testi require.DeepEqual(t, []byte("B"), prefs[0].Request.Auth.Message.Data) } -// Duplicate URLs with distinct auth_data must each pair with their own signed auth. -func TestBuildBuilderPreferencesForSlot_DuplicateURLDistinctAuthData(t *testing.T) { +// No two emitted entries may share a url; the first configured entry wins and +// pairs with the auth signed over its own auth_data. +func TestBuildBuilderPreferencesForSlot_DuplicateURLFirstEntryWins(t *testing.T) { pk := [fieldparams.BLSPubkeyLength]byte{2} slot := primitives.Slot(7) url := "https://builder.example" @@ -65,9 +66,8 @@ func TestBuildBuilderPreferencesForSlot_DuplicateURLDistinctAuthData(t *testing. } prefs := v.buildBuilderPreferencesForSlot(pk, slot) - require.Equal(t, 2, len(prefs)) + require.Equal(t, 1, len(prefs)) require.DeepEqual(t, []byte("A"), prefs[0].Request.Auth.Message.Data) - require.DeepEqual(t, []byte("B"), prefs[1].Request.Auth.Message.Data) } // Drives the real sign path (as warmBuilderRequestAuthsForDuties does) so a key-shape diff --git a/validator/client/validator.go b/validator/client/validator.go index 80358573252c..5cbc9820309e 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1494,11 +1494,17 @@ func (v *validator) submitBuilderPreferenceRequests(ctx context.Context, reqs [] func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Slot) []*ethpb.BuilderPreferenceV1 { targets := v.builderTargetsForKey(pk) prefs := make([]*ethpb.BuilderPreferenceV1, 0, len(targets)) + seen := make(map[string]bool, len(targets)) for _, t := range targets { + // No two entries may share a url; the first configured entry wins. + if seen[t.url] { + continue + } auth, ok := v.signedRequestAuthFor(pk, t.url, t.authData, slot) if !ok { continue } + seen[t.url] = true p := ðpb.BuilderPreferenceV1{ Request: ðpb.BuilderPreferencesRequestV1{ Preferences: ðpb.BuilderPreferencesV1{MaxExecutionPayment: primitives.Gwei(t.maxPayment)}, From feb11d0efcb776c402c1b66f1e27d7091586ea75 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 11:14:15 -0500 Subject: [PATCH 09/19] error was handled incorrectly updating --- .../rpc/eth/validator/builder_preferences.go | 13 ++-- .../eth/validator/builder_preferences_test.go | 75 +++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index ac96cde38de0..fd00a11228e7 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -2,12 +2,11 @@ package validator import ( "encoding/json" - "fmt" "io" "net/http" "strconv" - "strings" + "github.com/OffchainLabs/prysm/v7/api/server" "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/shared" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" @@ -163,18 +162,22 @@ func (s *Server) SubmitBuilderPreferences(w http.ResponseWriter, r *http.Request return } - var failures []string + var failures []*server.IndexedError for i, e := range entries { req, err := e.toSubmitRequest(pubkey) if err == nil { _, err = s.V1Alpha1Server.SubmitBuilderPreferences(ctx, req) } if err != nil { - failures = append(failures, fmt.Sprintf("entry %d: %v", i, err)) + failures = append(failures, &server.IndexedError{Index: i, Message: err.Error()}) } } if len(failures) > 0 { - httputil.HandleError(w, strings.Join(failures, "; "), http.StatusBadRequest) + httputil.WriteError(w, &server.IndexedErrorContainer{ + Code: http.StatusBadRequest, + Message: "One or more preference submissions were rejected", + Failures: failures, + }) return } w.WriteHeader(http.StatusOK) diff --git a/beacon-chain/rpc/eth/validator/builder_preferences_test.go b/beacon-chain/rpc/eth/validator/builder_preferences_test.go index d572a3df870f..9bc38ae43612 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences_test.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences_test.go @@ -2,10 +2,19 @@ package validator import ( "bytes" + "encoding/json" + "net/http" "net/http/httptest" "testing" + blockchainTesting "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing" + mockSync "github.com/OffchainLabs/prysm/v7/beacon-chain/sync/initial-sync/testing" + eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" + "github.com/OffchainLabs/prysm/v7/testing/assert" + mock2 "github.com/OffchainLabs/prysm/v7/testing/mock" "github.com/OffchainLabs/prysm/v7/testing/require" + "github.com/pkg/errors" + "go.uber.org/mock/gomock" ) // The produce-block body is a bare BuilderEntry array; malformed entries are @@ -61,3 +70,69 @@ func TestParseBuilderPreferencesBody_DuplicateURL(t *testing.T) { _, err := parseBuilderPreferencesBody(r) require.ErrorContains(t, "share the same url", err) } + +func TestSubmitBuilderPreferencesHandler(t *testing.T) { + pubkey := "0x" + repeatHex(48) + entry := func(url string) string { + return `{"url":"` + url + `","auth":{"message":{"data":"0x01","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"max_execution_payment":"1000"}` + } + newServer := func(t *testing.T, v1alpha1 eth.BeaconNodeValidatorServer) *Server { + return &Server{ + V1Alpha1Server: v1alpha1, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + OptimisticModeFetcher: &blockchainTesting.ChainService{}, + } + } + post := func(t *testing.T, s *Server, body string) *httptest.ResponseRecorder { + r := httptest.NewRequest(http.MethodPost, "http://foo.example/eth/v1/validator/builder_preferences/"+pubkey, bytes.NewBufferString(body)) + r.SetPathValue("pubkey", pubkey) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + s.SubmitBuilderPreferences(w, r) + return w + } + + t.Run("all entries forwarded", func(t *testing.T) { + ctrl := gomock.NewController(t) + v1alpha1 := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1.EXPECT().SubmitBuilderPreferences(gomock.Any(), gomock.Any()).Return(nil, nil).Times(2) + w := post(t, newServer(t, v1alpha1), `[`+entry("https://a")+`,`+entry("https://b")+`]`) + assert.Equal(t, http.StatusOK, w.Code) + }) + t.Run("failures reported by index as IndexedErrorMessage", func(t *testing.T) { + ctrl := gomock.NewController(t) + v1alpha1 := mock2.NewMockBeaconNodeValidatorServer(ctrl) + gomock.InOrder( + v1alpha1.EXPECT().SubmitBuilderPreferences(gomock.Any(), gomock.Any()).Return(nil, errors.New("boom")), + v1alpha1.EXPECT().SubmitBuilderPreferences(gomock.Any(), gomock.Any()).Return(nil, nil), + ) + w := post(t, newServer(t, v1alpha1), `[`+entry("https://a")+`,`+entry("https://b")+`]`) + assert.Equal(t, http.StatusBadRequest, w.Code) + + var resp struct { + Code int `json:"code"` + Message string `json:"message"` + Failures []struct { + Index int `json:"index"` + Message string `json:"message"` + } `json:"failures"` + } + require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp)) + require.Equal(t, http.StatusBadRequest, resp.Code) + require.Equal(t, 1, len(resp.Failures)) + require.Equal(t, 0, resp.Failures[0].Index) + require.StringContains(t, "boom", resp.Failures[0].Message) + }) + t.Run("malformed entry fails its own index only", func(t *testing.T) { + ctrl := gomock.NewController(t) + v1alpha1 := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1.EXPECT().SubmitBuilderPreferences(gomock.Any(), gomock.Any()).Return(nil, nil).Times(1) + w := post(t, newServer(t, v1alpha1), `[{"url":""},`+entry("https://b")+`]`) + assert.Equal(t, http.StatusBadRequest, w.Code) + }) + t.Run("empty body is a 400", func(t *testing.T) { + ctrl := gomock.NewController(t) + w := post(t, newServer(t, mock2.NewMockBeaconNodeValidatorServer(ctrl)), `[]`) + assert.Equal(t, http.StatusBadRequest, w.Code) + }) +} From 97451274af69604f5f12633107970c85a8af765f Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 14:03:25 -0500 Subject: [PATCH 10/19] updating error --- beacon-chain/rpc/eth/validator/builder_preferences.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index fd00a11228e7..b7fcd7b1f9ac 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -175,7 +175,7 @@ func (s *Server) SubmitBuilderPreferences(w http.ResponseWriter, r *http.Request if len(failures) > 0 { httputil.WriteError(w, &server.IndexedErrorContainer{ Code: http.StatusBadRequest, - Message: "One or more preference submissions were rejected", + Message: "Errors with one or more preference submissions; well-formed entries were still submitted", Failures: failures, }) return From 97eba9426e19dc80c36550b9344fa4433bdec384 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 14:55:15 -0500 Subject: [PATCH 11/19] copilot suggestions --- .../rpc/prysm/v1alpha1/validator/proposer.go | 10 ++++++++- .../prysm/v1alpha1/validator/proposer_bid.go | 16 ++++++++++++++ .../validator/proposer_gloas_prefs_test.go | 22 +++++++++++++++++++ .../client/beacon-api/propose_beacon_block.go | 2 +- 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go index b85ff5bb2128..c993b5818d59 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer.go @@ -357,7 +357,15 @@ func (vs *Server) ProposeBeaconBlock(ctx context.Context, req *ethpb.GenericSign // Empty means self-build or a P2P bid; the builder learns those via gossip. if block.Version() >= version.Gloas && req.BuilderUrl != "" { - go vs.submitBlockToBuilder(block, req.BuilderUrl) + // The URL originates from the client; only dial well-formed http(s) targets. + if validBuilderURL(req.BuilderUrl) { + go vs.submitBlockToBuilder(block, req.BuilderUrl) + } else { + log.WithFields(logrus.Fields{ + "slot": block.Block().Slot(), + "builderUrl": safeURLForLog(req.BuilderUrl), + }).Warn("Ignoring malformed builder url on publish") + } } if err := <-errChan; err != nil { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index 1dc9f2ce5a33..ea91d5c7acfa 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -4,6 +4,8 @@ import ( "context" "fmt" "math" + "math/big" + "net/url" "strings" "time" @@ -161,6 +163,20 @@ func builderBeatsLocal(builderValue, localValue primitives.Gwei, boostFactor uin return lhs.Cmp(rhs) > 0 } +func validBuilderURL(raw string) bool { + u, err := url.Parse(raw) + return err == nil && (u.Scheme == "http" || u.Scheme == "https") && u.Host != "" +} + +// safeURLForLog never echoes an unparseable value: MaskCredentialsLogging falls +// back to the raw input on parse failure, which could leak embedded credentials. +func safeURLForLog(raw string) string { + if _, err := url.Parse(raw); err != nil { + return "" + } + return logs.MaskCredentialsLogging(raw) +} + // The proposer's total take, the execution payment counts only up to the proposer's max preference. // Saturates instead of wrapping: a malicious bid must not rank below honest ones by overflowing. func effectiveBidValue(bid *ethpb.SignedExecutionPayloadBid, maxExecutionPayment uint64) primitives.Gwei { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go index 9c0780f7ece9..a562fa81ee53 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go @@ -1,6 +1,7 @@ package validator import ( + "strings" "testing" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" @@ -77,3 +78,24 @@ func TestBuilderAuthsAndPrefs(t *testing.T) { require.Equal(t, uint64(neutralBuilderBoostFactor), bp.boostFactor) }) } + +func TestValidBuilderURL(t *testing.T) { + valid := []string{"https://builder.example", "http://10.0.0.5:18550", "https://user@builder.example/path"} + for _, u := range valid { + require.Equal(t, true, validBuilderURL(u), u) + } + invalid := []string{"", "not a url", "file:///etc/passwd", "gopher://x", "https://", "//missing-scheme"} + for _, u := range invalid { + require.Equal(t, false, validBuilderURL(u), u) + } +} + +// The masking fallback echoes raw input on parse failure, so unparseable values +// must never reach it. +func TestSafeURLForLog(t *testing.T) { + // Space in host is one of the few inputs url.Parse rejects outright. + unparseable := "http://user:secret@ho st" + require.Equal(t, "", safeURLForLog(unparseable)) + require.Equal(t, false, strings.Contains(safeURLForLog(unparseable), "secret")) + require.Equal(t, false, strings.Contains(safeURLForLog("https://user:secret@builder.example/path"), "secret")) +} diff --git a/validator/client/beacon-api/propose_beacon_block.go b/validator/client/beacon-api/propose_beacon_block.go index cbbfb8823d55..1ac15d082c41 100644 --- a/validator/client/beacon-api/propose_beacon_block.go +++ b/validator/client/beacon-api/propose_beacon_block.go @@ -4,9 +4,9 @@ import ( "bytes" "context" "encoding/json" - "github.com/OffchainLabs/prysm/v7/api" "net/http" + "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server/structs" "github.com/OffchainLabs/prysm/v7/encoding/ssz" "github.com/OffchainLabs/prysm/v7/network/httputil" From 47d3aba1ae8a84a51a103eeeb6348bb59ef1e45b Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 16:16:07 -0500 Subject: [PATCH 12/19] make sure builder boost is per builder --- .../prysm/v1alpha1/validator/proposer_bid.go | 101 ++++++++++-------- .../validator/proposer_bid_builder_test.go | 55 +++++----- .../validator/proposer_bid_prefs_test.go | 89 +++------------ .../v1alpha1/validator/proposer_bid_test.go | 16 +-- .../v1alpha1/validator/proposer_gloas.go | 50 ++++----- .../validator/proposer_gloas_prefs_test.go | 70 ++++++------ 6 files changed, 167 insertions(+), 214 deletions(-) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index ea91d5c7acfa..a4b9ef13513b 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -54,7 +54,7 @@ func (vs *Server) setExecutionPayloadBid( sBlk interfaces.SignedBeaconBlock, local *consensusblocks.GetPayloadResponse, builderBid *ethpb.SignedExecutionPayloadBid, - prefs bidPreferences, + builderEff primitives.Gwei, selfBuildOnly bool, ) (bidSource, error) { _, span := trace.StartSpan(ctx, "ProposerServer.setExecutionPayloadBid") @@ -66,15 +66,15 @@ func (vs *Server) setExecutionPayloadBid( if !selfBuildOnly { p2pBid := vs.cachedP2PBid(sBlk, local) - if bestBid, src := bestBid(local, p2pBid, builderBid, prefs); bestBid != nil { - if err := sBlk.SetSignedExecutionPayloadBid(bestBid); err != nil { + if winner, winnerValue, src := bestBid(local, p2pBid, builderBid, builderEff); winner != nil { + if err := sBlk.SetSignedExecutionPayloadBid(winner); err != nil { return bidSourceSelfBuild, errors.Wrap(err, "could not set remote execution payload bid") } log.WithFields(logrus.Fields{ "slot": sBlk.Block().Slot(), "source": src, - "builder": bestBid.Message.BuilderIndex, - "valueGwei": uint64(effectiveBidValue(bestBid, prefs.maxPayment)), + "builder": winner.Message.BuilderIndex, + "valueGwei": uint64(winnerValue), }).Info("Chose payload bid") return src, nil } @@ -114,39 +114,29 @@ type bidPreferences struct { boostFactor uint64 } -// Returns a nil bid when the local self-build wins. A non-local bid must clear the -// min_bid floor and beat the local bid after the boost factor is applied. +// Returns a nil bid when the local self-build wins. The builder-API bid arrives +// already gated under its own entry's preferences; the P2P bid is outside entry +// governance and competes on raw value. func bestBid( local *consensusblocks.GetPayloadResponse, p2pBid *ethpb.SignedExecutionPayloadBid, builderBid *ethpb.SignedExecutionPayloadBid, - prefs bidPreferences, -) (*ethpb.SignedExecutionPayloadBid, bidSource) { - var bestBid *ethpb.SignedExecutionPayloadBid - var bestValue primitives.Gwei + builderEff primitives.Gwei, +) (*ethpb.SignedExecutionPayloadBid, primitives.Gwei, bidSource) { + var winner *ethpb.SignedExecutionPayloadBid + var winnerValue primitives.Gwei localValue := primitives.WeiToGwei(local.Bid) src := bidSourceSelfBuild - consider := func(bid *ethpb.SignedExecutionPayloadBid, from bidSource) { - if bid == nil { - return - } - value := effectiveBidValue(bid, prefs.maxPayment) - if uint64(value) < prefs.minBid { - return - } - if !builderBeatsLocal(value, localValue, prefs.boostFactor) { - return - } - if bestBid == nil || value > bestValue { - bestBid, bestValue, src = bid, value, from + if p2pBid != nil { + if v := p2pBid.Message.Value; v > localValue { + winner, winnerValue, src = p2pBid, v, bidSourceP2P } } - - consider(p2pBid, bidSourceP2P) - consider(builderBid, bidSourceBuilderAPI) - - return bestBid, src + if builderBid != nil && (winner == nil || builderEff > winnerValue) { + winner, winnerValue, src = builderBid, builderEff, bidSourceBuilderAPI + } + return winner, winnerValue, src } // builderBeatsLocal reports whether a boosted non-local bid beats the local bid. @@ -197,24 +187,39 @@ type builderBidQuery struct { parentRoot [32]byte parentHash [32]byte pubkey [fieldparams.BLSPubkeyLength]byte - maxPayment uint64 + localValue primitives.Gwei feeRecipient []byte parentGasLimit uint64 targetGasLimit uint64 - authsByURL map[string]*ethpb.SignedRequestAuthV1 + prefsByURL map[string]builderPref +} + +// builderPref pairs one builder's signed auth with its own bid preferences. +type builderPref struct { + auth *ethpb.SignedRequestAuthV1 + prefs bidPreferences } -func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state.BeaconState, q *builderBidQuery) (*ethpb.SignedExecutionPayloadBid, string) { - if vs.BlockBuilder == nil || len(q.authsByURL) == 0 { - return nil, "" +// builderBidQualifies applies one entry's floor and boost gate to its bid. +func builderBidQualifies(eff, localValue primitives.Gwei, p bidPreferences) bool { + return uint64(eff) >= p.minBid && builderBeatsLocal(eff, localValue, p.boostFactor) +} + +func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state.BeaconState, q *builderBidQuery) (*ethpb.SignedExecutionPayloadBid, string, primitives.Gwei) { + if vs.BlockBuilder == nil || len(q.prefsByURL) == 0 { + return nil, "", 0 + } + authsByURL := make(map[string]*ethpb.SignedRequestAuthV1, len(q.prefsByURL)) + for u, p := range q.prefsByURL { + authsByURL[u] = p.auth } ctx, cancel := context.WithTimeout(ctx, builderBidTimeout) defer cancel() - bids, err := vs.BlockBuilder.GetExecutionPayloadBid(ctx, q.slot, q.parentHash, q.parentRoot, q.pubkey, q.authsByURL) + bids, err := vs.BlockBuilder.GetExecutionPayloadBid(ctx, q.slot, q.parentHash, q.parentRoot, q.pubkey, authsByURL) if err != nil { builderGetPayloadMissCount.Inc() log.WithError(err).Error("Could not get builder execution payload bid") - return nil, "" + return nil, "", 0 } var ( @@ -227,11 +232,21 @@ func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state. if pb.Bid == nil { continue } - if err := vs.validateBuilderBid(head, pb.Bid, q); err != nil { + // Each bid is judged under its own entry's preferences. + pref, ok := q.prefsByURL[pb.BuilderURL] + if !ok { + continue + } + if err := vs.validateBuilderBid(head, pb.Bid, q, pref.prefs.maxPayment); err != nil { bidLog = append(bidLog, fmt.Sprintf("%s(builder=%d discarded: %v)", logs.MaskCredentialsLogging(pb.BuilderURL), pb.Bid.Message.BuilderIndex, err)) continue } - value := effectiveBidValue(pb.Bid, q.maxPayment) + value := effectiveBidValue(pb.Bid, pref.prefs.maxPayment) + if !builderBidQualifies(value, q.localValue, pref.prefs) { + bidLog = append(bidLog, fmt.Sprintf("%s(builder=%d below floor or boost gate: effective=%d)", + logs.MaskCredentialsLogging(pb.BuilderURL), pb.Bid.Message.BuilderIndex, value)) + continue + } bidLog = append(bidLog, fmt.Sprintf("%s(builder=%d value=%d payment=%d effective=%d)", logs.MaskCredentialsLogging(pb.BuilderURL), pb.Bid.Message.BuilderIndex, pb.Bid.Message.Value, pb.Bid.Message.ExecutionPayment, value)) if best == nil || value > bestValue { @@ -245,19 +260,19 @@ func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state. if best == nil { builderGetPayloadMissCount.Inc() - return nil, "" + return nil, "", 0 } - return best, bestURL + return best, bestURL, bestValue } // validateBuilderBid mirrors process_execution_payload_bid so a chosen bid never invalidates the proposer's own block. -func (vs *Server) validateBuilderBid(head state.BeaconState, signed *ethpb.SignedExecutionPayloadBid, q *builderBidQuery) error { +func (vs *Server) validateBuilderBid(head state.BeaconState, signed *ethpb.SignedExecutionPayloadBid, q *builderBidQuery, maxPayment uint64) error { if signed == nil || signed.Message == nil { return errors.New("nil builder bid") } bid := signed.Message - if uint64(bid.ExecutionPayment) > q.maxPayment { - return errors.Errorf("bid execution payment %d exceeds max %d", bid.ExecutionPayment, q.maxPayment) + if uint64(bid.ExecutionPayment) > maxPayment { + return errors.Errorf("bid execution payment %d exceeds max %d", bid.ExecutionPayment, maxPayment) } if vs.NewExecutionPayloadBidVerifier == nil { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go index 415761632945..1a4354bd7753 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go @@ -115,18 +115,19 @@ func TestBestBid(t *testing.T) { {name: "no remote bids", local: localWithGwei(100), wantSrc: bidSourceSelfBuild, wantNil: true}, {name: "p2p beats local", local: localWithGwei(0), p2p: newBid(1000, 0, p2pIdx), wantSrc: bidSourceP2P}, {name: "p2p below local", local: localWithGwei(2000), p2p: newBid(1000, 0, p2pIdx), wantSrc: bidSourceSelfBuild, wantNil: true}, - {name: "builder beats local", local: localWithGwei(0), builder: newBid(1000, 0, builderIdx), maxPayment: 1000, wantSrc: bidSourceBuilderAPI}, - {name: "builder below local", local: localWithGwei(2000), builder: newBid(1000, 0, builderIdx), maxPayment: 1000, wantSrc: bidSourceSelfBuild, wantNil: true}, + // Builder bids arrive pre-gated; bestBid only ranks them against p2p. + {name: "pre-gated builder present wins alone", local: localWithGwei(2000), builder: newBid(1000, 0, builderIdx), maxPayment: 1000, wantSrc: bidSourceBuilderAPI}, {name: "builder beats p2p", local: localWithGwei(0), p2p: newBid(1000, 0, p2pIdx), builder: newBid(2000, 0, builderIdx), maxPayment: 1000, wantSrc: bidSourceBuilderAPI}, {name: "p2p beats builder", local: localWithGwei(0), p2p: newBid(2000, 0, p2pIdx), builder: newBid(1000, 0, builderIdx), maxPayment: 1000, wantSrc: bidSourceP2P}, {name: "tie prefers p2p", local: localWithGwei(0), p2p: newBid(1000, 0, p2pIdx), builder: newBid(1000, 0, builderIdx), maxPayment: 1000, wantSrc: bidSourceP2P}, - {name: "payment cap keeps local ahead", local: localWithGwei(100), builder: newBid(50, 100, builderIdx), maxPayment: 40, wantSrc: bidSourceSelfBuild, wantNil: true}, - {name: "payment within cap wins", local: localWithGwei(100), builder: newBid(50, 100, builderIdx), maxPayment: 60, wantSrc: bidSourceBuilderAPI}, - {name: "saturated bid still beats local", local: localWithGwei(100), builder: newBid(math.MaxUint64-5, 100, builderIdx), maxPayment: math.MaxUint64, wantSrc: bidSourceBuilderAPI}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, src := bestBid(tt.local, tt.p2p, tt.builder, bidPreferences{maxPayment: tt.maxPayment, boostFactor: 100}) + var eff primitives.Gwei + if tt.builder != nil { + eff = effectiveBidValue(tt.builder, tt.maxPayment) + } + got, _, src := bestBid(tt.local, tt.p2p, tt.builder, eff) require.Equal(t, tt.wantSrc, src) if tt.wantNil { require.IsNil(t, got) @@ -166,29 +167,28 @@ func TestValidateBuilderBid(t *testing.T) { } } - query := func(maxPayment uint64) *builderBidQuery { + query := func() *builderBidQuery { return &builderBidQuery{ slot: slot, parentRoot: parentRoot, parentHash: parentHash, - maxPayment: maxPayment, } } t.Run("nil bid", func(t *testing.T) { vs := &Server{} - require.ErrorContains(t, "nil builder bid", vs.validateBuilderBid(head, nil, query(1000))) + require.ErrorContains(t, "nil builder bid", vs.validateBuilderBid(head, nil, query(), 1000)) }) t.Run("payment exceeds max", func(t *testing.T) { vs := &Server{} - err := vs.validateBuilderBid(head, fullBid(), query(50)) + err := vs.validateBuilderBid(head, fullBid(), query(), 50) require.ErrorContains(t, "exceeds max", err) }) t.Run("verifier not ready", func(t *testing.T) { vs := &Server{} - err := vs.validateBuilderBid(head, fullBid(), query(1000)) + err := vs.validateBuilderBid(head, fullBid(), query(), 1000) require.ErrorContains(t, "bid verifier not ready", err) }) @@ -198,7 +198,7 @@ func TestValidateBuilderBid(t *testing.T) { captured = &fakeBidVerifier{} return captured }} - require.NoError(t, vs.validateBuilderBid(head, fullBid(), query(1000))) + require.NoError(t, vs.validateBuilderBid(head, fullBid(), query(), 1000)) // The parent-linkage closures must match only the block being produced. require.Equal(t, true, captured.rootSeenFn(parentRoot)) @@ -212,7 +212,7 @@ func TestValidateBuilderBid(t *testing.T) { vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { return &fakeBidVerifier{sigErr: errors.New("bad signature")} }} - err := vs.validateBuilderBid(head, fullBid(), query(1000)) + err := vs.validateBuilderBid(head, fullBid(), query(), 1000) require.ErrorContains(t, "bad signature", err) }) @@ -220,7 +220,7 @@ func TestValidateBuilderBid(t *testing.T) { vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { return &fakeBidVerifier{feeErr: errors.New("fee recipient mismatch")} }} - err := vs.validateBuilderBid(head, fullBid(), query(1000)) + err := vs.validateBuilderBid(head, fullBid(), query(), 1000) require.ErrorContains(t, "fee recipient mismatch", err) }) @@ -228,7 +228,7 @@ func TestValidateBuilderBid(t *testing.T) { vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { return &fakeBidVerifier{gasErr: errors.New("gas limit incompatible")} }} - err := vs.validateBuilderBid(head, fullBid(), query(1000)) + err := vs.validateBuilderBid(head, fullBid(), query(), 1000) require.ErrorContains(t, "gas limit incompatible", err) }) } @@ -238,7 +238,7 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { parentRoot := [32]byte{1, 2, 3} parentHash := [32]byte{9, 9, 9} pubkey := [48]byte{4, 5, 6} - auths := []*ethpb.SignedRequestAuthV1{{}} + auths := []*ethpb.SignedRequestAuthV1{{Message: ðpb.RequestAuthV1{Data: []byte("http://builder")}}} head, err := util.NewBeaconStateGloas() require.NoError(t, err) @@ -264,29 +264,32 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { return &fakeBidVerifier{} } query := func(auths []*ethpb.SignedRequestAuthV1) *builderBidQuery { - byURL := make(map[string]*ethpb.SignedRequestAuthV1, len(auths)) + byURL := make(map[string]builderPref, len(auths)) for _, a := range auths { - byURL[string(a.GetMessage().GetData())] = a + byURL[string(a.GetMessage().GetData())] = builderPref{ + auth: a, + prefs: bidPreferences{maxPayment: 1000, boostFactor: 100}, + } } return &builderBidQuery{ slot: slot, parentRoot: parentRoot, parentHash: parentHash, pubkey: pubkey, - authsByURL: byURL, + prefsByURL: byURL, } } t.Run("no builder configured", func(t *testing.T) { vs := &Server{} - got, url := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) + got, url, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) require.IsNil(t, got) require.Equal(t, "", url) }) t.Run("no auths", func(t *testing.T) { vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} - got, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(nil)) + got, _, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(nil)) require.IsNil(t, got) }) @@ -295,7 +298,7 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { BlockBuilder: &builderTest.MockBuilderService{PayloadBids: []beaconbuilder.PayloadBid{bid(1, 500), bid(2, 1500), bid(3, 900)}}, NewExecutionPayloadBidVerifier: passAll, } - got, url := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) + got, url, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) require.NotNil(t, got) require.Equal(t, primitives.BuilderIndex(2), got.Message.BuilderIndex) require.Equal(t, "http://builder", url) @@ -313,7 +316,7 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { return &fakeBidVerifier{} }, } - got, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) + got, _, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) require.NotNil(t, got) require.Equal(t, primitives.BuilderIndex(1), got.Message.BuilderIndex) }) @@ -325,7 +328,7 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { return &fakeBidVerifier{activeErr: errors.New("not active")} }, } - got, url := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) + got, url, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) require.IsNil(t, got) require.Equal(t, "", url) }) @@ -335,7 +338,7 @@ func TestGetBuilderExecutionPayloadBid(t *testing.T) { BlockBuilder: &builderTest.MockBuilderService{ErrGetExecutionPayloadBid: errors.New("boom")}, NewExecutionPayloadBidVerifier: passAll, } - got, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) + got, _, _ := vs.getBuilderExecutionPayloadBid(t.Context(), head, query(auths)) require.IsNil(t, got) }) } @@ -395,7 +398,7 @@ func TestSetExecutionPayloadBid_PrefersBuilderBid(t *testing.T) { // No P2P cache, so the Builder-API bid is the only remote candidate. vs := &Server{} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, builderBid, bidPreferences{maxPayment: 1000, boostFactor: 100}, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, builderBid, effectiveBidValue(builderBid, 1000), false) require.NoError(t, err) require.Equal(t, bidSourceBuilderAPI, src) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go index 59418c818afc..bc9a5e6f5149 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_prefs_test.go @@ -6,87 +6,32 @@ import ( "math" "testing" - consensusblocks "github.com/OffchainLabs/prysm/v7/consensus-types/blocks" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" - ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/require" ) -// TestBestBid_BoostFactor exercises the boost-factor and min-bid preferences that -// gate whether a builder bid can beat the local self-build. -func TestBestBid_BoostFactor(t *testing.T) { - const builderIdx = primitives.BuilderIndex(2) +// TestBuilderBidGate exercises the per-entry floor and boost gate each builder +// bid must clear against the local self-build before ranking. +func TestBuilderBidGate(t *testing.T) { tests := []struct { - name string - local *consensusblocks.GetPayloadResponse - builder *ethpb.SignedExecutionPayloadBid - prefs bidPreferences - wantSrc bidSource - wantNil bool + name string + eff primitives.Gwei + local primitives.Gwei + prefs bidPreferences + want bool }{ - { - name: "neutral 100 higher builder wins", - local: localWithGwei(100), - builder: newBid(1000, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: 100}, - wantSrc: bidSourceBuilderAPI, - }, - { - name: "neutral 100 lower builder loses", - local: localWithGwei(1000), - builder: newBid(100, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: 100}, - wantSrc: bidSourceSelfBuild, - wantNil: true, - }, - { - name: "boost 0 prefers local even when builder is higher", - local: localWithGwei(0), - builder: newBid(1000, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: 0}, - wantSrc: bidSourceSelfBuild, - wantNil: true, - }, - { - name: "boost max prefers builder even when local is higher", - local: localWithGwei(1000), - builder: newBid(1, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: math.MaxUint64}, - wantSrc: bidSourceBuilderAPI, - }, - { - name: "boost 200 lets 60 beat local 100", - local: localWithGwei(100), - builder: newBid(60, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: 200}, - wantSrc: bidSourceBuilderAPI, - }, - { - name: "min_bid rejects builder below floor", - local: localWithGwei(0), - builder: newBid(50, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: 100, minBid: 100}, - wantSrc: bidSourceSelfBuild, - wantNil: true, - }, - { - name: "min_bid allows builder at floor", - local: localWithGwei(0), - builder: newBid(100, 0, builderIdx), - prefs: bidPreferences{maxPayment: 1000, boostFactor: 100, minBid: 100}, - wantSrc: bidSourceBuilderAPI, - }, + {name: "neutral 100 higher wins", eff: 1000, local: 100, prefs: bidPreferences{boostFactor: 100}, want: true}, + {name: "neutral 100 lower loses", eff: 100, local: 1000, prefs: bidPreferences{boostFactor: 100}, want: false}, + {name: "boost 0 never wins", eff: 1000, local: 0, prefs: bidPreferences{boostFactor: 0}, want: false}, + {name: "boost max always wins", eff: 1, local: 1000, prefs: bidPreferences{boostFactor: math.MaxUint64}, want: true}, + {name: "boost 200 lets 60 beat 100", eff: 60, local: 100, prefs: bidPreferences{boostFactor: 200}, want: true}, + {name: "below min_bid rejected", eff: 99, local: 0, prefs: bidPreferences{boostFactor: 100, minBid: 100}, want: false}, + {name: "at min_bid accepted", eff: 100, local: 0, prefs: bidPreferences{boostFactor: 100, minBid: 100}, want: true}, + {name: "min_bid gates max boost", eff: 99, local: 0, prefs: bidPreferences{boostFactor: math.MaxUint64, minBid: 100}, want: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, src := bestBid(tt.local, nil, tt.builder, tt.prefs) - require.Equal(t, tt.wantSrc, src) - if tt.wantNil { - require.IsNil(t, got) - return - } - require.NotNil(t, got) - require.Equal(t, builderIdx, got.Message.BuilderIndex) + require.Equal(t, tt.want, builderBidQualifies(tt.eff, tt.local, tt.prefs)) }) } } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go index 3802c5fe4463..bb9db8628b6b 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_test.go @@ -56,7 +56,7 @@ func TestSetSelfBuildExecutionPayloadBid(t *testing.T) { vs := &Server{} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) @@ -125,7 +125,7 @@ func TestSetSelfBuildExecutionPayloadBid_BlobCommitments(t *testing.T) { } vs := &Server{} - _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, true) + _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, true) require.NoError(t, err) signedBid, err := sBlk.Block().Body().SignedExecutionPayloadBid() @@ -149,10 +149,10 @@ func TestSetSelfBuildExecutionPayloadBid_NilPayload(t *testing.T) { vs := &Server{} - _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, nil, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) + _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, nil, nil, 0, false) require.ErrorContains(t, "local execution payload is nil", err) - _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, &consensusblocks.GetPayloadResponse{}, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) + _, err = vs.setExecutionPayloadBid(t.Context(), sBlk, &consensusblocks.GetPayloadResponse{}, nil, 0, false) require.ErrorContains(t, "local execution payload is nil", err) } @@ -215,7 +215,7 @@ func TestSetExecutionPayloadBid_PrefersP2PBid(t *testing.T) { vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) require.NoError(t, err) require.Equal(t, bidSourceP2P, src) @@ -289,7 +289,7 @@ func TestSetExecutionPayloadBid_PrefersLocalWhenHigherValue(t *testing.T) { vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) @@ -362,7 +362,7 @@ func TestSetExecutionPayloadBid_SelfBuildOnlyIgnoresCache(t *testing.T) { vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, true) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, true) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) @@ -414,7 +414,7 @@ func TestSetExecutionPayloadBid_FallsBackToSelfBuildWhenNoCachedBid(t *testing.T bidCache := cache.NewHighestExecutionPayloadBidCache() vs := &Server{HighestBidCache: bidCache} - src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, bidPreferences{maxPayment: 0, boostFactor: 100}, false) + src, err := vs.setExecutionPayloadBid(t.Context(), sBlk, local, nil, 0, false) require.NoError(t, err) require.Equal(t, bidSourceSelfBuild, src) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go index 7864f6565e9a..a54d2d1f0582 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go @@ -46,7 +46,7 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea selfBuildOnly := local.OverrideBuilder || skipBuilder var builderBid *ethpb.SignedExecutionPayloadBid var builderURL string - prefs := bidPreferences{boostFactor: neutralBuilderBoostFactor} + var builderEff primitives.Gwei if !selfBuildOnly && len(builderPreferences) > 0 { val, valErr := head.ValidatorAtIndexReadOnly(sBlk.Block().ProposerIndex()) parentGasLimit, glErr := vs.ForkchoiceFetcher.GasLimit(sBlk.Block().ParentRoot()) @@ -57,24 +57,22 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea log.WithError(glErr).Error("Could not get parent gas limit for builder bid request") default: pubkey := val.PublicKey() - var authsByURL map[string]*ethpb.SignedRequestAuthV1 - authsByURL, prefs = builderAuthsAndPrefs(builderPreferences) pref := vs.proposerPreferenceForProposal(ctx, head, sBlk.Block().Slot(), sBlk.Block().ProposerIndex()) feeRecipient := pref.FeeRecipientOrDefault() - builderBid, builderURL = vs.getBuilderExecutionPayloadBid(ctx, head, &builderBidQuery{ + builderBid, builderURL, builderEff = vs.getBuilderExecutionPayloadBid(ctx, head, &builderBidQuery{ slot: sBlk.Block().Slot(), parentRoot: sBlk.Block().ParentRoot(), parentHash: bytesutil.ToBytes32(local.ExecutionData.ParentHash()), pubkey: pubkey, - maxPayment: prefs.maxPayment, + localValue: primitives.WeiToGwei(local.Bid), feeRecipient: feeRecipient[:], parentGasLimit: parentGasLimit, targetGasLimit: pref.GasLimitOr(parentGasLimit), - authsByURL: authsByURL, + prefsByURL: builderPrefsByURL(builderPreferences), }) } } - src, bidErr := vs.setExecutionPayloadBid(ctx, sBlk, local, builderBid, prefs, selfBuildOnly) + src, bidErr := vs.setExecutionPayloadBid(ctx, sBlk, local, builderBid, builderEff, selfBuildOnly) if bidErr != nil { return nil, status.Errorf(codes.Internal, "Could not set execution payload bid: %v", bidErr) } @@ -123,32 +121,28 @@ func (vs *Server) buildBlockGloas(ctx context.Context, sBlk interfaces.SignedBea return blk, nil } -// builderAuthsAndPrefs extracts the signed request auths and the effective -// proposer-local bid preferences from the inline builder preferences carried on -// the block request. The effective preferences use the first valid entry as the -// proposal baseline. The beacon node enforces the caps locally during bid -// selection; communicating preferences to the builder is left to the bid request. -func builderAuthsAndPrefs(prefs []*ethpb.BuilderPreferenceV1) (map[string]*ethpb.SignedRequestAuthV1, bidPreferences) { - authsByURL := make(map[string]*ethpb.SignedRequestAuthV1, len(prefs)) - bp := bidPreferences{boostFactor: neutralBuilderBoostFactor} - baselineSet := false +// builderPrefsByURL indexes each builder entry's auth and bid preferences by +// url; every builder's bid is later judged under its own entry's values. +func builderPrefsByURL(prefs []*ethpb.BuilderPreferenceV1) map[string]builderPref { + out := make(map[string]builderPref, len(prefs)) for _, p := range prefs { if p == nil || p.Request == nil || p.Request.Auth == nil || p.Url == "" { continue } - if _, ok := authsByURL[p.Url]; !ok { - authsByURL[p.Url] = p.Request.Auth + if _, ok := out[p.Url]; ok { + continue } - if !baselineSet { - bp.maxPayment = uint64(p.Request.Preferences.GetMaxExecutionPayment()) - if p.MinBid != nil { - bp.minBid = uint64(p.GetMinBid()) - } - if p.BuilderBoostFactor != nil { - bp.boostFactor = p.GetBuilderBoostFactor() - } - baselineSet = true + bp := bidPreferences{ + maxPayment: uint64(p.Request.Preferences.GetMaxExecutionPayment()), + boostFactor: neutralBuilderBoostFactor, + } + if p.MinBid != nil { + bp.minBid = uint64(p.GetMinBid()) + } + if p.BuilderBoostFactor != nil { + bp.boostFactor = p.GetBuilderBoostFactor() } + out[p.Url] = builderPref{auth: p.Request.Auth, prefs: bp} } - return authsByURL, bp + return out } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go index a562fa81ee53..7c68781f94d7 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas_prefs_test.go @@ -25,57 +25,53 @@ func prefEntry(url string, maxPayment uint64, minBid *uint64, boost *uint64) *et return p } -func TestBuilderAuthsAndPrefs(t *testing.T) { +func TestBuilderPrefsByURL(t *testing.T) { minBid := uint64(7) boost := uint64(120) - t.Run("empty input yields no auths and neutral prefs", func(t *testing.T) { - auths, bp := builderAuthsAndPrefs(nil) - require.Equal(t, 0, len(auths)) - require.Equal(t, uint64(0), bp.maxPayment) - require.Equal(t, uint64(0), bp.minBid) - require.Equal(t, uint64(neutralBuilderBoostFactor), bp.boostFactor) + t.Run("empty input yields empty map", func(t *testing.T) { + require.Equal(t, 0, len(builderPrefsByURL(nil))) }) - t.Run("malformed entries are skipped and never set the baseline", func(t *testing.T) { + t.Run("each url keeps its own preferences", func(t *testing.T) { + otherMin := uint64(1) + otherBoost := uint64(999) + out := builderPrefsByURL([]*ethpb.BuilderPreferenceV1{ + prefEntry("https://a", 100, &minBid, &boost), + prefEntry("https://b", 200, &otherMin, &otherBoost), + }) + require.Equal(t, 2, len(out)) + require.Equal(t, uint64(100), out["https://a"].prefs.maxPayment) + require.Equal(t, minBid, out["https://a"].prefs.minBid) + require.Equal(t, boost, out["https://a"].prefs.boostFactor) + require.Equal(t, uint64(200), out["https://b"].prefs.maxPayment) + require.Equal(t, otherMin, out["https://b"].prefs.minBid) + require.Equal(t, otherBoost, out["https://b"].prefs.boostFactor) + }) + t.Run("malformed entries are skipped", func(t *testing.T) { badMin := uint64(999) - entries := []*ethpb.BuilderPreferenceV1{ + out := builderPrefsByURL([]*ethpb.BuilderPreferenceV1{ nil, {Url: "https://no-request", MinBid: func() *primitives.Gwei { g := primitives.Gwei(badMin); return &g }()}, {Url: "https://no-auth", Request: ðpb.BuilderPreferencesRequestV1{}}, func() *ethpb.BuilderPreferenceV1 { p := prefEntry("", 5, &badMin, nil); return p }(), prefEntry("https://good", 42, &minBid, &boost), - } - auths, bp := builderAuthsAndPrefs(entries) - require.Equal(t, 1, len(auths)) - require.NotNil(t, auths["https://good"]) - require.Equal(t, uint64(42), bp.maxPayment) - require.Equal(t, minBid, bp.minBid) - require.Equal(t, boost, bp.boostFactor) - }) - t.Run("first valid entry sets the baseline, later entries do not override", func(t *testing.T) { - otherMin := uint64(1) - otherBoost := uint64(999) - auths, bp := builderAuthsAndPrefs([]*ethpb.BuilderPreferenceV1{ - prefEntry("https://a", 100, &minBid, &boost), - prefEntry("https://b", 200, &otherMin, &otherBoost), }) - require.Equal(t, 2, len(auths)) - require.Equal(t, uint64(100), bp.maxPayment) - require.Equal(t, minBid, bp.minBid) - require.Equal(t, boost, bp.boostFactor) + require.Equal(t, 1, len(out)) + require.NotNil(t, out["https://good"].auth) }) - t.Run("duplicate url keeps the first auth", func(t *testing.T) { - first := prefEntry("https://dup", 1, nil, nil) - second := prefEntry("https://dup", 2, nil, nil) - auths, _ := builderAuthsAndPrefs([]*ethpb.BuilderPreferenceV1{first, second}) - require.Equal(t, 1, len(auths)) - require.Equal(t, first.Request.Auth, auths["https://dup"]) + t.Run("duplicate url keeps the first entry", func(t *testing.T) { + out := builderPrefsByURL([]*ethpb.BuilderPreferenceV1{ + prefEntry("https://dup", 1, nil, nil), + prefEntry("https://dup", 2, nil, nil), + }) + require.Equal(t, 1, len(out)) + require.Equal(t, uint64(1), out["https://dup"].prefs.maxPayment) }) t.Run("unset min_bid and boost fall to client defaults", func(t *testing.T) { - _, bp := builderAuthsAndPrefs([]*ethpb.BuilderPreferenceV1{prefEntry("https://a", 5, nil, nil)}) - require.Equal(t, uint64(5), bp.maxPayment) - require.Equal(t, uint64(0), bp.minBid) - require.Equal(t, uint64(neutralBuilderBoostFactor), bp.boostFactor) + out := builderPrefsByURL([]*ethpb.BuilderPreferenceV1{prefEntry("https://a", 5, nil, nil)}) + require.Equal(t, uint64(5), out["https://a"].prefs.maxPayment) + require.Equal(t, uint64(0), out["https://a"].prefs.minBid) + require.Equal(t, uint64(neutralBuilderBoostFactor), out["https://a"].prefs.boostFactor) }) } From b788882d78d0165b4f8e07544a7cbfb47b84adc9 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 21 Jul 2026 16:22:36 -0500 Subject: [PATCH 13/19] regenerate validator mocks Co-Authored-By: Claude Fable 5 --- testing/validator-mock/validator_mock.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/testing/validator-mock/validator_mock.go b/testing/validator-mock/validator_mock.go index de5bbf2de79c..d1524d1d1c09 100644 --- a/testing/validator-mock/validator_mock.go +++ b/testing/validator-mock/validator_mock.go @@ -254,6 +254,18 @@ func (mr *MockValidatorMockRecorder) LogValidatorGainsAndLosses(ctx, slot any) * return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LogValidatorGainsAndLosses", reflect.TypeOf((*MockValidator)(nil).LogValidatorGainsAndLosses), ctx, slot) } +// MaybeRetryMissingNextDuties mocks base method. +func (m *MockValidator) MaybeRetryMissingNextDuties(ctx context.Context, slot primitives.Slot) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "MaybeRetryMissingNextDuties", ctx, slot) +} + +// MaybeRetryMissingNextDuties indicates an expected call of MaybeRetryMissingNextDuties. +func (mr *MockValidatorMockRecorder) MaybeRetryMissingNextDuties(ctx, slot any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybeRetryMissingNextDuties", reflect.TypeOf((*MockValidator)(nil).MaybeRetryMissingNextDuties), ctx, slot) +} + // NextSlot mocks base method. func (m *MockValidator) NextSlot() <-chan primitives.Slot { m.ctrl.T.Helper() @@ -491,18 +503,6 @@ func (mr *MockValidatorMockRecorder) UpdateDuties(ctx any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDuties", reflect.TypeOf((*MockValidator)(nil).UpdateDuties), ctx) } -// MaybeRetryMissingNextDuties mocks base method. -func (m *MockValidator) MaybeRetryMissingNextDuties(arg0 context.Context, arg1 primitives.Slot) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "MaybeRetryMissingNextDuties", arg0, arg1) -} - -// MaybeRetryMissingNextDuties indicates an expected call of MaybeRetryMissingNextDuties. -func (mr *MockValidatorMockRecorder) MaybeRetryMissingNextDuties(arg0, arg1 any) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaybeRetryMissingNextDuties", reflect.TypeOf((*MockValidator)(nil).MaybeRetryMissingNextDuties), arg0, arg1) -} - // WaitForActivation mocks base method. func (m *MockValidator) WaitForActivation(ctx context.Context) error { m.ctrl.T.Helper() From b67300f07275163dcfa4647dac475e8b989ce145 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Wed, 22 Jul 2026 11:12:26 -0500 Subject: [PATCH 14/19] fixing bugs such as dedupe builders on settings, adding missed builder public key, removing relay field --- .../rpc/eth/validator/builder_preferences.go | 7 + .../eth/validator/builder_preferences_test.go | 10 + .../prysm/v1alpha1/validator/proposer_bid.go | 19 +- .../validator/proposer_bid_builder_test.go | 41 +++- .../v1alpha1/validator/proposer_gloas.go | 1 + .../james-prysm_validator-config-endpoint.md | 8 +- .../proposer/effective_builder_config_test.go | 21 -- config/proposer/loader/loader.go | 6 + config/proposer/loader/loader_test.go | 3 +- config/proposer/settings.go | 50 ++-- config/proposer/settings_test.go | 37 ++- proto/prysm/v1alpha1/gloas_builder_api.pb.go | 27 ++- proto/prysm/v1alpha1/gloas_builder_api.proto | 2 + .../validator-client/keymanager.pb.go | 220 +++++++++--------- .../validator-client/keymanager.proto | 3 +- .../client/beacon-api/builder_preferences.go | 4 + validator/client/request_auth_test.go | 60 +++++ validator/client/runner_test.go | 1 - validator/client/validator.go | 92 +++++--- validator/client/validator_test.go | 54 ++++- validator/db/convert_test.go | 3 - validator/db/filesystem/db.go | 17 -- validator/rpc/handlers_validator_config.go | 5 + .../rpc/handlers_validator_config_test.go | 16 ++ 24 files changed, 457 insertions(+), 250 deletions(-) diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index b7fcd7b1f9ac..07547a050563 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -86,6 +86,13 @@ func (p *builderEntryJson) toConsensus() (*eth.BuilderPreferenceV1, error) { Url: p.Url, Request: ð.BuilderPreferencesRequestV1{Preferences: ð.BuilderPreferencesV1{}, Auth: auth}, } + if p.Pubkey != "" { + pk, err := hexutil.Decode(p.Pubkey) + if err != nil || len(pk) != fieldparams.BLSPubkeyLength { + return nil, errors.New("pubkey is not a valid BLS public key") + } + out.Pubkey = pk + } if p.MaxExecutionPayment != "" { v, err := strconv.ParseUint(p.MaxExecutionPayment, 10, 64) if err != nil { diff --git a/beacon-chain/rpc/eth/validator/builder_preferences_test.go b/beacon-chain/rpc/eth/validator/builder_preferences_test.go index 9bc38ae43612..1f39eff458f7 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences_test.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences_test.go @@ -42,6 +42,16 @@ func TestParseBuilderPreferencesBody(t *testing.T) { require.Equal(t, 1, len(prefs)) require.Equal(t, "https://b", prefs[0].Url) }) + t.Run("pubkey is parsed and bad pubkeys are skipped", func(t *testing.T) { + withKey := `{"url":"https://k","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"pubkey":"0x` + repeatHex(48) + `"}` + badKey := `{"url":"https://short","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"pubkey":"0x0102"}` + r := httptest.NewRequest("POST", "/", bytes.NewBufferString(`[`+withKey+`,`+badKey+`]`)) + prefs, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 1, len(prefs)) + require.Equal(t, "https://k", prefs[0].Url) + require.Equal(t, 48, len(prefs[0].Pubkey)) + }) t.Run("empty body yields no preferences", func(t *testing.T) { r := httptest.NewRequest("POST", "/", bytes.NewBuffer(nil)) prefs, err := parseBuilderPreferencesBody(r) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go index a4b9ef13513b..2a20c4697c38 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid.go @@ -1,6 +1,7 @@ package validator import ( + "bytes" "context" "fmt" "math" @@ -112,6 +113,7 @@ type bidPreferences struct { maxPayment uint64 minBid uint64 boostFactor uint64 + pubkey []byte } // Returns a nil bid when the local self-build wins. The builder-API bid arrives @@ -237,7 +239,7 @@ func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state. if !ok { continue } - if err := vs.validateBuilderBid(head, pb.Bid, q, pref.prefs.maxPayment); err != nil { + if err := vs.validateBuilderBid(head, pb.Bid, q, pref.prefs); err != nil { bidLog = append(bidLog, fmt.Sprintf("%s(builder=%d discarded: %v)", logs.MaskCredentialsLogging(pb.BuilderURL), pb.Bid.Message.BuilderIndex, err)) continue } @@ -266,13 +268,22 @@ func (vs *Server) getBuilderExecutionPayloadBid(ctx context.Context, head state. } // validateBuilderBid mirrors process_execution_payload_bid so a chosen bid never invalidates the proposer's own block. -func (vs *Server) validateBuilderBid(head state.BeaconState, signed *ethpb.SignedExecutionPayloadBid, q *builderBidQuery, maxPayment uint64) error { +func (vs *Server) validateBuilderBid(head state.BeaconState, signed *ethpb.SignedExecutionPayloadBid, q *builderBidQuery, prefs bidPreferences) error { if signed == nil || signed.Message == nil { return errors.New("nil builder bid") } bid := signed.Message - if uint64(bid.ExecutionPayment) > maxPayment { - return errors.Errorf("bid execution payment %d exceeds max %d", bid.ExecutionPayment, maxPayment) + if uint64(bid.ExecutionPayment) > prefs.maxPayment { + return errors.Errorf("bid execution payment %d exceeds max %d", bid.ExecutionPayment, prefs.maxPayment) + } + if len(prefs.pubkey) > 0 { + builderPubkey, err := head.BuilderPubkey(bid.BuilderIndex) + if err != nil { + return errors.Wrap(err, "could not get builder pubkey") + } + if !bytes.Equal(builderPubkey[:], prefs.pubkey) { + return errors.New("bid is not signed by the configured builder pubkey") + } } if vs.NewExecutionPayloadBidVerifier == nil { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go index 1a4354bd7753..5ba55e6874aa 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bid_builder_test.go @@ -177,28 +177,55 @@ func TestValidateBuilderBid(t *testing.T) { t.Run("nil bid", func(t *testing.T) { vs := &Server{} - require.ErrorContains(t, "nil builder bid", vs.validateBuilderBid(head, nil, query(), 1000)) + require.ErrorContains(t, "nil builder bid", vs.validateBuilderBid(head, nil, query(), bidPreferences{maxPayment: 1000})) }) t.Run("payment exceeds max", func(t *testing.T) { vs := &Server{} - err := vs.validateBuilderBid(head, fullBid(), query(), 50) + err := vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 50}) require.ErrorContains(t, "exceeds max", err) }) t.Run("verifier not ready", func(t *testing.T) { vs := &Server{} - err := vs.validateBuilderBid(head, fullBid(), query(), 1000) + err := vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 1000}) require.ErrorContains(t, "bid verifier not ready", err) }) + t.Run("pubkey binding", func(t *testing.T) { + key := make([]byte, 48) + key[0] = 0xAB + builders := make([]*ethpb.Builder, 4) + for i := range builders { + builders[i] = ðpb.Builder{Pubkey: make([]byte, 48)} + } + builders[3] = ðpb.Builder{Pubkey: key} + bound, err := util.NewBeaconStateGloas(func(st *ethpb.BeaconStateGloas) error { + st.Builders = builders + return nil + }) + require.NoError(t, err) + + vs := &Server{} + err = vs.validateBuilderBid(bound, fullBid(), query(), bidPreferences{maxPayment: 1000, pubkey: make([]byte, 48)}) + require.ErrorContains(t, "not signed by the configured builder pubkey", err) + + // A matching key passes the binding and proceeds to the verifier gate. + err = vs.validateBuilderBid(bound, fullBid(), query(), bidPreferences{maxPayment: 1000, pubkey: key}) + require.ErrorContains(t, "bid verifier not ready", err) + + // An index with no registry entry fails closed. + err = vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 1000, pubkey: key}) + require.ErrorContains(t, "could not get builder pubkey", err) + }) + t.Run("all checks pass", func(t *testing.T) { var captured *fakeBidVerifier vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { captured = &fakeBidVerifier{} return captured }} - require.NoError(t, vs.validateBuilderBid(head, fullBid(), query(), 1000)) + require.NoError(t, vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 1000})) // The parent-linkage closures must match only the block being produced. require.Equal(t, true, captured.rootSeenFn(parentRoot)) @@ -212,7 +239,7 @@ func TestValidateBuilderBid(t *testing.T) { vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { return &fakeBidVerifier{sigErr: errors.New("bad signature")} }} - err := vs.validateBuilderBid(head, fullBid(), query(), 1000) + err := vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 1000}) require.ErrorContains(t, "bad signature", err) }) @@ -220,7 +247,7 @@ func TestValidateBuilderBid(t *testing.T) { vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { return &fakeBidVerifier{feeErr: errors.New("fee recipient mismatch")} }} - err := vs.validateBuilderBid(head, fullBid(), query(), 1000) + err := vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 1000}) require.ErrorContains(t, "fee recipient mismatch", err) }) @@ -228,7 +255,7 @@ func TestValidateBuilderBid(t *testing.T) { vs := &Server{NewExecutionPayloadBidVerifier: func(interfaces.ROSignedExecutionPayloadBid, []verification.Requirement) verification.ExecutionPayloadBidVerifier { return &fakeBidVerifier{gasErr: errors.New("gas limit incompatible")} }} - err := vs.validateBuilderBid(head, fullBid(), query(), 1000) + err := vs.validateBuilderBid(head, fullBid(), query(), bidPreferences{maxPayment: 1000}) require.ErrorContains(t, "gas limit incompatible", err) }) } diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go index a54d2d1f0582..c01520340b6c 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_gloas.go @@ -135,6 +135,7 @@ func builderPrefsByURL(prefs []*ethpb.BuilderPreferenceV1) map[string]builderPre bp := bidPreferences{ maxPayment: uint64(p.Request.Preferences.GetMaxExecutionPayment()), boostFactor: neutralBuilderBoostFactor, + pubkey: p.GetPubkey(), } if p.MinBid != nil { bp.minBid = uint64(p.GetMinBid()) diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index 9e313b7df3dd..510a3d816c18 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -6,10 +6,13 @@ ### Changed -- Resolve per-key builder config with field-level inheritance from `default_config`; `enabled` and `max_execution_payment` are presence-tracked so unset values inherit. -- Per-key builder settings no longer require a per-key fee recipient to register; the default fee recipient is used. +- Resolve per-key builder config with field-level inheritance from `default_config`; `enabled` and `max_execution_payment` are presence-tracked so unset values inherit. Applies to v2 settings only — v1 settings keep the existing registration semantics unchanged. +- For v2 settings, per-key builder settings no longer require a per-key fee recipient to register; the default fee recipient is used. - Send builder preferences inline with the block request instead of caching them on the beacon node; request auths sign opaque `auth_data` per builder-specs #165, with the builder URL carried separately. - Merge file- or URL-loaded proposer settings per key: the file only overrides keys it names, and the settings schema version never regresses. +- Reject duplicate builder urls when setting the validator config; previously persisted duplicates keep the first entry for each url. + +- Enforce the per-builder `pubkey` from the validator config during bid selection: when set, only bids from that builder key are accepted for the entry's url. ### Fixed @@ -18,3 +21,4 @@ ### Removed - Remove the beacon-node builder-preference caches; `BlockRequest.builder_request_auths` is replaced by `builder_preferences`. +- Remove the unused `relays` field from the proposer settings builder config; a settings file that still contains it logs a warning and the field is ignored. diff --git a/config/proposer/effective_builder_config_test.go b/config/proposer/effective_builder_config_test.go index e76ecc2a012a..4c46ed42c6c0 100644 --- a/config/proposer/effective_builder_config_test.go +++ b/config/proposer/effective_builder_config_test.go @@ -95,27 +95,6 @@ func TestEffectiveBuilderConfig(t *testing.T) { perKey := &BuilderConfig{GasLimit: validator.Uint64(45000000)} require.Equal(t, validator.Uint64(45000000), EffectiveBuilderConfig(perKey, def).GasLimit) }) - t.Run("present relays replace default relays", func(t *testing.T) { - def := &BuilderConfig{Relays: []string{"https://r-def"}} - perKey := &BuilderConfig{Relays: []string{"https://r-key"}} - eff := EffectiveBuilderConfig(perKey, def) - require.Equal(t, 1, len(eff.Relays)) - require.Equal(t, "https://r-key", eff.Relays[0]) - }) - t.Run("absent relays inherit default relays", func(t *testing.T) { - def := &BuilderConfig{Relays: []string{"https://r-def"}} - perKey := &BuilderConfig{Enabled: proto.Bool(true)} - require.Equal(t, 1, len(EffectiveBuilderConfig(perKey, def).Relays)) - }) - // Builders and relays are independent fields; setting one per-key does not - // suppress inheriting the other. - t.Run("per-key relays plus default builders compose", func(t *testing.T) { - def := &BuilderConfig{Builders: []*BuilderEntry{entryA}} - perKey := &BuilderConfig{Relays: []string{"https://r-key"}} - eff := EffectiveBuilderConfig(perKey, def) - require.Equal(t, 1, len(eff.Builders)) - require.Equal(t, 1, len(eff.Relays)) - }) t.Run("nil nil is nil", func(t *testing.T) { require.Equal(t, (*BuilderConfig)(nil), EffectiveBuilderConfig(nil, nil)) }) diff --git a/config/proposer/loader/loader.go b/config/proposer/loader/loader.go index c2664d34734e..cb9cf5e0040f 100644 --- a/config/proposer/loader/loader.go +++ b/config/proposer/loader/loader.go @@ -1,8 +1,11 @@ package loader import ( + "bytes" "encoding/json" "fmt" + "os" + "path/filepath" "strconv" "github.com/OffchainLabs/prysm/v7/cmd/validator/flags" @@ -220,6 +223,9 @@ func (psl *SettingsLoader) loadFromFile(cliCtx *cli.Context, dbSettings *validat if settingFromFile == nil { return nil, errors.Errorf("proposer settings is empty after unmarshalling from file specified by %s flag", flags.ProposerSettingsFlag.Name) } + if raw, err := os.ReadFile(filepath.Clean(cliCtx.String(flags.ProposerSettingsFlag.Name))); err == nil && bytes.Contains(raw, []byte("relays")) { + log.Warn("The relays field in proposer settings is no longer supported and was ignored; configure builders instead") + } log.WithField(flags.ProposerSettingsFlag.Name, cliCtx.String(flags.ProposerSettingsFlag.Name)).Info("Proposer settings loaded from file") return psl.processProposerSettings(settingFromFile, dbSettings), nil } diff --git a/config/proposer/loader/loader_test.go b/config/proposer/loader/loader_test.go index d4c041c36ed8..a25f2114b580 100644 --- a/config/proposer/loader/loader_test.go +++ b/config/proposer/loader/loader_test.go @@ -997,7 +997,6 @@ func Test_ProposerSettingsLoaderWithOnlyBuilder_DoesNotSaveInDB(t *testing.T) { BuilderConfig: &proposer.BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), - Relays: nil, }, }, } @@ -1287,7 +1286,7 @@ func Test_mergeProposerSettings_V2GasLimitOnlyGoesToOption(t *testing.T) { func Test_mergeProposerSettings_VersionGatesBuilderReset(t *testing.T) { v1Builder := func() *validatorpb.BuilderConfig { - return &validatorpb.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 40000000, Relays: []string{"r"}} + return &validatorpb.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 40000000} } t.Run("v1 db without enable-builder drops DB builder", func(t *testing.T) { db := &validatorpb.ProposerSettingsPayload{ diff --git a/config/proposer/settings.go b/config/proposer/settings.go index 2e916114dda6..b040d7392412 100644 --- a/config/proposer/settings.go +++ b/config/proposer/settings.go @@ -64,9 +64,37 @@ func SettingFromConsensus(ps *validatorpb.ProposerSettingsPayload) (*Settings, e settings.DefaultConfig = d } settings.normalizeLegacyPresence() + settings.dedupBuilders() return settings, nil } +// Persisted configs may predate duplicate-url rejection; the first entry wins, +// matching inline preference assembly. +func (ps *Settings) dedupBuilders() { + fix := func(opt *Option) { + if opt == nil || opt.BuilderConfig == nil || len(opt.BuilderConfig.Builders) < 2 { + return + } + seen := make(map[string]bool, len(opt.BuilderConfig.Builders)) + kept := opt.BuilderConfig.Builders[:0] + for _, e := range opt.BuilderConfig.Builders { + if e == nil || seen[e.URL] { + continue + } + seen[e.URL] = true + kept = append(kept, e) + } + if len(kept) != len(opt.BuilderConfig.Builders) { + log.Warn("Duplicate builder urls in proposer settings; keeping the first entry for each url") + opt.BuilderConfig.Builders = kept + } + } + fix(ps.DefaultConfig) + for _, opt := range ps.ProposeConfig { + fix(opt) + } +} + // v1 payloads predate presence tracking: wire-absent enabled meant disabled, and // the filesystem backend persisted a spurious explicit 0 max execution payment. func (ps *Settings) normalizeLegacyPresence() { @@ -110,7 +138,6 @@ func verifyOption(key string, option *validatorpb.ProposerOptionPayload) error { type BuilderConfig struct { Enabled *bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` GasLimit validator.Uint64 `json:"gas_limit,omitempty" yaml:"gas_limit,omitempty"` - Relays []string `json:"relays,omitempty" yaml:"relays,omitempty"` MaxExecutionPayment *validator.Uint64 `json:"max_execution_payment,omitempty" yaml:"max_execution_payment,omitempty"` // explicit 0 = trustless-only; unset inherits Builders []*BuilderEntry `json:"builders,omitempty" yaml:"builders,omitempty"` Proxy *string `json:"proxy,omitempty" yaml:"proxy,omitempty"` @@ -137,7 +164,7 @@ func (bc *BuilderConfig) IsEnabled() bool { } // EffectiveBuilderConfig resolves per-key builder config with field-level -// inheritance; the builders/relays lists replace rather than merge. +// inheritance; the builders list replaces rather than merges. func EffectiveBuilderConfig(perKey, def *BuilderConfig) *BuilderConfig { if perKey == nil { return def @@ -153,7 +180,6 @@ func EffectiveBuilderConfig(perKey, def *BuilderConfig) *BuilderConfig { BuilderBoostFactor: coalesceUint64(perKey.BuilderBoostFactor, def.BuilderBoostFactor), Proxy: coalesceString(perKey.Proxy, def.Proxy), Builders: perKey.Builders, - Relays: perKey.Relays, } if eff.GasLimit == 0 { eff.GasLimit = def.GasLimit @@ -161,9 +187,6 @@ func EffectiveBuilderConfig(perKey, def *BuilderConfig) *BuilderConfig { if len(eff.Builders) == 0 { eff.Builders = def.Builders } - if len(eff.Relays) == 0 { - eff.Relays = def.Relays - } return eff } @@ -201,11 +224,6 @@ func BuilderConfigFromConsensus(from *validatorpb.BuilderConfig) *BuilderConfig MinBid: cloneUint64(from.MinBid), BuilderBoostFactor: cloneUint64(from.BuilderBoostFactor), } - if from.Relays != nil { - relays := make([]string, len(from.Relays)) - copy(relays, from.Relays) - c.Relays = relays - } if len(from.Builders) > 0 { c.Builders = make([]*BuilderEntry, 0, len(from.Builders)) for _, b := range from.Builders { @@ -374,11 +392,6 @@ func (bc *BuilderConfig) Clone() *BuilderConfig { c.Proxy = cloneString(bc.Proxy) c.MinBid = cloneUint64(bc.MinBid) c.BuilderBoostFactor = cloneUint64(bc.BuilderBoostFactor) - if bc.Relays != nil { - relays := make([]string, len(bc.Relays)) - copy(relays, bc.Relays) - c.Relays = relays - } if len(bc.Builders) > 0 { c.Builders = make([]*BuilderEntry, 0, len(bc.Builders)) for _, b := range bc.Builders { @@ -443,11 +456,6 @@ func (bc *BuilderConfig) ToConsensus() *validatorpb.BuilderConfig { } c := &validatorpb.BuilderConfig{} c.Enabled = cloneBool(bc.Enabled) - if bc.Relays != nil { - relays := make([]string, len(bc.Relays)) - copy(relays, bc.Relays) - c.Relays = relays - } c.GasLimit = bc.GasLimit c.MaxExecutionPayment = cloneUint64(bc.MaxExecutionPayment) c.Proxy = cloneString(bc.Proxy) diff --git a/config/proposer/settings_test.go b/config/proposer/settings_test.go index 1ca27252a630..b1a23c8b8eb7 100644 --- a/config/proposer/settings_test.go +++ b/config/proposer/settings_test.go @@ -12,6 +12,7 @@ import ( "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/consensus-types/validator" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" + validatorpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1/validator-client" "github.com/OffchainLabs/prysm/v7/testing/assert" "github.com/OffchainLabs/prysm/v7/testing/require" ) @@ -34,7 +35,6 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), - Relays: []string{"https://example-relay.com"}, MaxExecutionPayment: uint64ValPtr(1000000000), }, }, @@ -46,7 +46,6 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(false), GasLimit: validator.Uint64(params.BeaconConfig().DefaultBuilderGasLimit), - Relays: []string{"https://example-relay.com"}, MaxExecutionPayment: uint64ValPtr(2000000000), }, }, @@ -73,7 +72,6 @@ func Test_Proposer_Setting_Cloning(t *testing.T) { t.Run("Happy Path BuilderConfigFromConsensus", func(t *testing.T) { clone := settings.DefaultConfig.BuilderConfig.Clone() config := BuilderConfigFromConsensus(clone.ToConsensus()) - require.DeepEqual(t, config.Relays, clone.Relays) require.DeepEqual(t, config.Enabled, clone.Enabled) require.Equal(t, config.GasLimit, clone.GasLimit) require.DeepEqual(t, config.MaxExecutionPayment, clone.MaxExecutionPayment) @@ -123,7 +121,6 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), - Relays: []string{"https://example-relay.com"}, }, }, }, @@ -142,7 +139,6 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), - Relays: []string{"https://example-relay.com"}, }, }, }, @@ -159,7 +155,6 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), - Relays: []string{"https://example-relay.com"}, }, }, }, @@ -170,7 +165,6 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), - Relays: []string{"https://example-relay.com"}, }, }, }, @@ -203,7 +197,6 @@ func TestProposerSettings_ShouldBeSaved(t *testing.T) { BuilderConfig: &BuilderConfig{ Enabled: proto.Bool(true), GasLimit: validator.Uint64(40000000), - Relays: []string{"https://example-relay.com"}, }, }, }, @@ -514,7 +507,7 @@ func TestSettings_UpgradeToV2(t *testing.T) { t.Run("v1 default lifts BuilderConfig.GasLimit to top-level and retains builder relays", func(t *testing.T) { ps := &Settings{ DefaultConfig: &Option{ - BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(42_000_000), Relays: []string{"http://b:8080"}}, + BuilderConfig: &BuilderConfig{Enabled: proto.Bool(true), GasLimit: validator.Uint64(42_000_000)}, }, } require.Equal(t, true, ps.UpgradeToV2()) @@ -522,8 +515,6 @@ func TestSettings_UpgradeToV2(t *testing.T) { require.Equal(t, validator.Uint64(42_000_000), ps.DefaultConfig.GasLimit) // BuilderConfig is retained so the gloas builder-API relays/enabled survive the upgrade. require.NotNil(t, ps.DefaultConfig.BuilderConfig) - require.Equal(t, 1, len(ps.DefaultConfig.BuilderConfig.Relays)) - require.Equal(t, "http://b:8080", ps.DefaultConfig.BuilderConfig.Relays[0]) }) t.Run("v1 top-level GasLimit already set is preserved", func(t *testing.T) { @@ -624,6 +615,30 @@ func TestSettings_TargetGasLimit(t *testing.T) { // Legacy (pre-v2) payloads can't express presence: wire-absent enabled meant // disabled, and the filesystem backend wrote spurious explicit-0 max payments. +// Persisted payloads may predate duplicate-url rejection at the API. +func TestSettingFromConsensus_DedupsBuilders(t *testing.T) { + payload := &validatorpb.ProposerSettingsPayload{ + Version: SchemaV2, + DefaultConfig: &validatorpb.ProposerOptionPayload{ + Builder: &validatorpb.BuilderConfig{ + Enabled: proto.Bool(true), + Builders: []*validatorpb.BuilderEntry{ + {Url: "https://b.example", AuthData: []byte("first")}, + {Url: "https://b.example", AuthData: []byte("second")}, + {Url: "https://other.example"}, + }, + }, + }, + } + ps, err := SettingFromConsensus(payload) + require.NoError(t, err) + builders := ps.DefaultConfig.BuilderConfig.Builders + require.Equal(t, 2, len(builders)) + require.Equal(t, "https://b.example", builders[0].URL) + require.DeepEqual(t, []byte("first"), builders[0].AuthData) + require.Equal(t, "https://other.example", builders[1].URL) +} + func TestSettingFromConsensus_NormalizesLegacyPresence(t *testing.T) { key := [fieldparams.BLSPubkeyLength]byte{9} legacy := &Settings{ diff --git a/proto/prysm/v1alpha1/gloas_builder_api.pb.go b/proto/prysm/v1alpha1/gloas_builder_api.pb.go index 6225d9a15174..90ec177ac7b0 100755 --- a/proto/prysm/v1alpha1/gloas_builder_api.pb.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.pb.go @@ -289,6 +289,7 @@ type BuilderPreferenceV1 struct { MinBid *github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei `protobuf:"varint,2,opt,name=min_bid,json=minBid,proto3,oneof" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei"` BuilderBoostFactor *uint64 `protobuf:"varint,3,opt,name=builder_boost_factor,json=builderBoostFactor,proto3,oneof" json:"builder_boost_factor,omitempty"` Url string `protobuf:"bytes,4,opt,name=url,proto3" json:"url,omitempty"` + Pubkey []byte `protobuf:"bytes,5,opt,name=pubkey,proto3" json:"pubkey,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -351,6 +352,13 @@ func (x *BuilderPreferenceV1) GetUrl() string { return "" } +func (x *BuilderPreferenceV1) GetPubkey() []byte { + if x != nil { + return x.Pubkey + } + return nil +} + var File_proto_prysm_v1alpha1_gloas_builder_api_proto protoreflect.FileDescriptor var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ @@ -407,7 +415,7 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, - 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xb5, 0x02, 0x0a, 0x13, 0x42, + 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xcd, 0x02, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x56, 0x31, 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, @@ -424,14 +432,15 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x48, 0x01, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x12, 0x10, 0x0a, 0x03, 0x75, - 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x42, 0x0a, 0x0a, - 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, - 0x6f, 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, - 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, + 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, + 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, + 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, + 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index 9bf0fb8b6125..2320034e57e9 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -61,4 +61,6 @@ message BuilderPreferenceV1 { optional uint64 builder_boost_factor = 3; // The signed auth data is opaque, so the builder URL is carried explicitly. string url = 4; + // When set, only bids signed by this builder key are accepted from this url. + bytes pubkey = 5; } diff --git a/proto/prysm/v1alpha1/validator-client/keymanager.pb.go b/proto/prysm/v1alpha1/validator-client/keymanager.pb.go index c81e88e0863e..91c690c0816a 100755 --- a/proto/prysm/v1alpha1/validator-client/keymanager.pb.go +++ b/proto/prysm/v1alpha1/validator-client/keymanager.pb.go @@ -715,7 +715,6 @@ type BuilderConfig struct { state protoimpl.MessageState `protogen:"open.v1"` Enabled *bool `protobuf:"varint,1,opt,name=enabled,proto3,oneof" json:"enabled,omitempty"` GasLimit github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,2,opt,name=gas_limit,json=gasLimit,proto3" json:"gas_limit,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` - Relays []string `protobuf:"bytes,3,rep,name=relays,proto3" json:"relays,omitempty"` MaxExecutionPayment *github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 `protobuf:"varint,4,opt,name=max_execution_payment,json=maxExecutionPayment,proto3,oneof" json:"max_execution_payment,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64"` Builders []*BuilderEntry `protobuf:"bytes,5,rep,name=builders,proto3" json:"builders,omitempty"` Proxy *string `protobuf:"bytes,6,opt,name=proxy,proto3,oneof" json:"proxy,omitempty"` @@ -769,13 +768,6 @@ func (x *BuilderConfig) GetGasLimit() github_com_OffchainLabs_prysm_v7_consensus return github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64(0) } -func (x *BuilderConfig) GetRelays() []string { - if x != nil { - return x.Relays - } - return nil -} - func (x *BuilderConfig) GetMaxExecutionPayment() github_com_OffchainLabs_prysm_v7_consensus_types_validator.Uint64 { if x != nil && x.MaxExecutionPayment != nil { return *x.MaxExecutionPayment @@ -1192,7 +1184,7 @@ var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDesc = []byte 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x42, 0x0b, 0x0a, 0x09, 0x5f, 0x67, 0x72, - 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x22, 0xc7, 0x05, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x61, 0x66, 0x66, 0x69, 0x74, 0x69, 0x22, 0xbd, 0x05, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x88, 0x01, 0x01, 0x12, 0x62, 0x0a, 0x09, 0x67, 0x61, 0x73, 0x5f, 0x6c, @@ -1201,116 +1193,116 @@ var file_proto_prysm_v1alpha1_validator_client_keymanager_proto_rawDesc = []byte 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, - 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x72, - 0x65, 0x6c, 0x61, 0x79, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x6c, - 0x61, 0x79, 0x73, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, - 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, - 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x01, 0x52, 0x13, 0x6d, 0x61, 0x78, - 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, - 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, - 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, - 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, - 0x62, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, - 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, - 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, - 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, - 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, - 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, - 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, - 0x36, 0x34, 0x48, 0x04, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, - 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, - 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, - 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, - 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, - 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, - 0x22, 0xbf, 0x04, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, - 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x88, 0x01, 0x01, - 0x12, 0x20, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x08, 0x61, 0x75, 0x74, 0x68, 0x44, 0x61, 0x74, 0x61, 0x88, - 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x48, 0x02, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, - 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, - 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, - 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, - 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, - 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, - 0x01, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x34, 0x52, 0x08, 0x67, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x7e, 0x0a, 0x15, 0x6d, + 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, + 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, + 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, + 0x34, 0x48, 0x01, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x48, 0x0a, 0x08, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x42, + 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x73, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x88, 0x01, 0x01, + 0x12, 0x63, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x04, 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, - 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x88, - 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, - 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, - 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, - 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x05, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, - 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, - 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, - 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, - 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, + 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, + 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, + 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, + 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, + 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x04, 0x52, 0x12, 0x62, 0x75, + 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x88, 0x01, 0x01, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, - 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, - 0x6f, 0x72, 0x22, 0x81, 0x03, 0x0a, 0x17, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, - 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x74, - 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, - 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, - 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, - 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, - 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x1a, 0x78, 0x0a, 0x13, - 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xcd, 0x01, 0x0a, 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x65, - 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x42, 0x0f, 0x4b, - 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, - 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, - 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, - 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3b, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, - 0x6f, 0x72, 0x70, 0x62, 0xaa, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, - 0x74, 0x73, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x1e, 0x45, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, - 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x5c, 0x41, 0x63, 0x63, 0x6f, 0x75, - 0x6e, 0x74, 0x73, 0x5c, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, + 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, + 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, + 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x52, 0x06, + 0x72, 0x65, 0x6c, 0x61, 0x79, 0x73, 0x22, 0xbf, 0x04, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x06, 0x70, 0x75, 0x62, + 0x6b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x08, 0x61, 0x75, 0x74, + 0x68, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x05, 0x70, 0x72, 0x6f, 0x78, 0x79, + 0x88, 0x01, 0x01, 0x12, 0x63, 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, + 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, + 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x03, 0x52, 0x06, 0x6d, + 0x69, 0x6e, 0x42, 0x69, 0x64, 0x88, 0x01, 0x01, 0x12, 0x7e, 0x0a, 0x15, 0x6d, 0x61, 0x78, 0x5f, + 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, + 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x04, + 0x52, 0x13, 0x6d, 0x61, 0x78, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, + 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x7c, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x42, 0x45, 0x82, 0xb5, 0x18, 0x41, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, + 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, + 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x48, 0x05, 0x52, + 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x88, 0x01, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x75, 0x62, 0x6b, 0x65, + 0x79, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x64, 0x61, 0x74, 0x61, 0x42, + 0x08, 0x0a, 0x06, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, + 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x65, 0x78, + 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x61, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x42, + 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, + 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x81, 0x03, 0x0a, 0x17, 0x50, 0x72, 0x6f, + 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x74, 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, + 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x4b, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, + 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, 0x70, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5c, 0x0a, 0x0e, 0x64, 0x65, + 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, + 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x1a, 0x78, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x70, 0x6f, 0x73, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x4b, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x35, 0x2e, 0x65, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x76, 0x32, 0x2e, 0x50, 0x72, 0x6f, 0x70, + 0x6f, 0x73, 0x65, 0x72, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0xcd, 0x01, 0x0a, + 0x22, 0x6f, 0x72, 0x67, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, + 0x2e, 0x76, 0x32, 0x42, 0x0f, 0x4b, 0x65, 0x79, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x72, 0x50, + 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x52, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, + 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, + 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3b, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, 0xaa, 0x02, 0x1e, 0x45, 0x74, 0x68, + 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x2e, + 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x1e, 0x45, 0x74, + 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x5c, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x5c, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/proto/prysm/v1alpha1/validator-client/keymanager.proto b/proto/prysm/v1alpha1/validator-client/keymanager.proto index 786882353ec0..4c55d25ae16a 100644 --- a/proto/prysm/v1alpha1/validator-client/keymanager.proto +++ b/proto/prysm/v1alpha1/validator-client/keymanager.proto @@ -134,7 +134,8 @@ message BuilderConfig { (ethereum.eth.ext.cast_type) = "github.com/OffchainLabs/prysm/v7/consensus-types/validator.Uint64" ]; - repeated string relays = 3; + reserved 3; + reserved "relays"; // max_execution_payment is the maximum execution layer payment (in Gwei) the // proposer will accept from a builder for the Gloas builder API. Explicit 0 // means trustless-only; unset inherits; 2^64-1 means accept any amount. diff --git a/validator/client/beacon-api/builder_preferences.go b/validator/client/beacon-api/builder_preferences.go index 96dc8fb09fd8..3aa806535732 100644 --- a/validator/client/beacon-api/builder_preferences.go +++ b/validator/client/beacon-api/builder_preferences.go @@ -16,6 +16,7 @@ import ( type builderEntryReq struct { Url string `json:"url"` Auth *signedAuthReq `json:"auth"` + Pubkey string `json:"pubkey,omitempty"` MaxExecutionPayment string `json:"max_execution_payment,omitempty"` MinBid string `json:"min_bid,omitempty"` BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` @@ -62,6 +63,9 @@ func marshalBuilderPreferences(prefs []*ethpb.BuilderPreferenceV1) ([]byte, erro Auth: signedAuthFromConsensus(p.Request.Auth), MaxExecutionPayment: strconv.FormatUint(uint64(p.Request.Preferences.GetMaxExecutionPayment()), 10), } + if len(p.Pubkey) > 0 { + entry.Pubkey = hexutil.Encode(p.Pubkey) + } if p.MinBid != nil { entry.MinBid = strconv.FormatUint(uint64(p.GetMinBid()), 10) } diff --git a/validator/client/request_auth_test.go b/validator/client/request_auth_test.go index 52c0ec5e5872..4c2d971cf9e3 100644 --- a/validator/client/request_auth_test.go +++ b/validator/client/request_auth_test.go @@ -4,14 +4,23 @@ import ( "testing" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" + "github.com/OffchainLabs/prysm/v7/config/params" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" validatortypes "github.com/OffchainLabs/prysm/v7/consensus-types/validator" ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/require" + logTest "github.com/sirupsen/logrus/hooks/test" "google.golang.org/protobuf/proto" ) +func setGloasForkEpoch(t *testing.T, epoch primitives.Epoch) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = epoch + params.OverrideBeaconConfig(cfg) +} + func cachedAuth(data []byte, slot primitives.Slot) *ethpb.SignedRequestAuthV1 { return ðpb.SignedRequestAuthV1{Message: ðpb.RequestAuthV1{Data: data, Slot: slot}} } @@ -27,6 +36,7 @@ func settingsWithBuilders(pk [fieldparams.BLSPubkeyLength]byte, entries ...*prop // A cached auth signed over rotated-away auth_data must never be attached to a proposal. func TestBuildBuilderPreferencesForSlot_StaleAuthNotServedAfterRotation(t *testing.T) { + setGloasForkEpoch(t, 0) pk := [fieldparams.BLSPubkeyLength]byte{1} slot := primitives.Slot(10) url := "https://builder.example" @@ -50,6 +60,7 @@ func TestBuildBuilderPreferencesForSlot_StaleAuthNotServedAfterRotation(t *testi // No two emitted entries may share a url; the first configured entry wins and // pairs with the auth signed over its own auth_data. func TestBuildBuilderPreferencesForSlot_DuplicateURLFirstEntryWins(t *testing.T) { + setGloasForkEpoch(t, 0) pk := [fieldparams.BLSPubkeyLength]byte{2} slot := primitives.Slot(7) url := "https://builder.example" @@ -70,9 +81,43 @@ func TestBuildBuilderPreferencesForSlot_DuplicateURLFirstEntryWins(t *testing.T) require.DeepEqual(t, []byte("A"), prefs[0].Request.Auth.Message.Data) } +// Pre-gloas proposals carry no inline preferences and emit no warn, even with +// builder targets configured. +func TestBuildBuilderPreferencesForSlot_PreGloasInert(t *testing.T) { + setGloasForkEpoch(t, 100) + logHook := logTest.NewGlobal() + pk := [fieldparams.BLSPubkeyLength]byte{3} + v := validator{ + proposerSettings: settingsWithBuilders(pk, &proposer.BuilderEntry{URL: "https://builder.example", AuthData: []byte("A")}), + } + require.Equal(t, 0, len(v.buildBuilderPreferencesForSlot(pk, primitives.Slot(10)))) + require.LogsDoNotContain(t, logHook, "no signed request auths are warmed") +} + +// The entry's builder pubkey rides the wire so the beacon node can enforce it. +func TestBuildBuilderPreferencesForSlot_CarriesEntryPubkey(t *testing.T) { + setGloasForkEpoch(t, 0) + pk := [fieldparams.BLSPubkeyLength]byte{4} + slot := primitives.Slot(9) + url := "https://builder.example" + builderKey := make([]byte, 48) + builderKey[0] = 0xAB + + v := validator{ + proposerSettings: settingsWithBuilders(pk, &proposer.BuilderEntry{URL: url, AuthData: []byte("A"), Pubkey: builderKey}), + signedRequestAuths: map[requestAuthKey]*ethpb.SignedRequestAuthV1{ + {pk: pk, slot: slot, relay: url, authData: "A"}: cachedAuth([]byte("A"), slot), + }, + } + prefs := v.buildBuilderPreferencesForSlot(pk, slot) + require.Equal(t, 1, len(prefs)) + require.DeepEqual(t, builderKey, prefs[0].Pubkey) +} + // Drives the real sign path (as warmBuilderRequestAuthsForDuties does) so a key-shape // mismatch between signRequestAuthCached and signedRequestAuthFor cannot go unnoticed. func TestSignRequestAuthCached_RotationRoundTrip(t *testing.T) { + setGloasForkEpoch(t, 0) kp := randKeypair(t) km := newMockKeymanager(t, kp) ctx := t.Context() @@ -107,6 +152,21 @@ func TestSignRequestAuthCached_RotationRoundTrip(t *testing.T) { } // Field-level inheritance vectors through target resolution (keymanager #87 discussion). +// Duplicate urls collapse at the target level so the AOT and inline channels +// always agree on the effective entry. +func TestBuilderTargetsForKey_DuplicateURLFirstWins(t *testing.T) { + pk := [fieldparams.BLSPubkeyLength]byte{5} + v := validator{ + proposerSettings: settingsWithBuilders(pk, + &proposer.BuilderEntry{URL: "https://builder.example", AuthData: []byte("A")}, + &proposer.BuilderEntry{URL: "https://builder.example", AuthData: []byte("B")}, + ), + } + targets := v.builderTargetsForKey(pk) + require.Equal(t, 1, len(targets)) + require.DeepEqual(t, []byte("A"), targets[0].authData) +} + func TestBuilderTargetsForKey_FieldLevelInheritance(t *testing.T) { pk := [fieldparams.BLSPubkeyLength]byte{4} minBid := validatortypes.Uint64(5000000) diff --git a/validator/client/runner_test.go b/validator/client/runner_test.go index 096145b1d4c6..da3de3465038 100644 --- a/validator/client/runner_test.go +++ b/validator/client/runner_test.go @@ -572,7 +572,6 @@ func TestRunnerPushesProposerSettings_ValidContext(t *testing.T) { BuilderConfig: &proposer.BuilderConfig{ Enabled: proto.Bool(true), GasLimit: 60_000_000, - Relays: []string{"https://example.com"}, }, GraffitiConfig: &proposer.GraffitiConfig{ Graffiti: "foobar", diff --git a/validator/client/validator.go b/validator/client/validator.go index f952d3c2252a..8fc8ba3cfaef 100644 --- a/validator/client/validator.go +++ b/validator/client/validator.go @@ -1366,6 +1366,7 @@ func (v *validator) submittedPrefSlotsCount() int { type builderTarget struct { url string authData []byte + pubkey []byte maxPayment uint64 minBid *uint64 boostFactor *uint64 @@ -1405,12 +1406,14 @@ func (v *validator) builderTargetsForKey(pk pubkey) []builderTarget { fbMin := uint64Ptr(bc.MinBid) fbBoost := uint64Ptr(bc.BuilderBoostFactor) - targets := make([]builderTarget, 0, len(bc.Builders)+len(bc.Relays)) + targets := make([]builderTarget, 0, len(bc.Builders)) + seen := make(map[string]bool, len(bc.Builders)) for _, e := range bc.Builders { - if e == nil || e.URL == "" { + if e == nil || e.URL == "" || seen[e.URL] { continue } - t := builderTarget{url: e.URL, authData: e.AuthData, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost} + seen[e.URL] = true + t := builderTarget{url: e.URL, authData: e.AuthData, pubkey: e.Pubkey, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost} if e.MaxExecutionPayment != nil { t.maxPayment = uint64(*e.MaxExecutionPayment) } @@ -1422,9 +1425,6 @@ func (v *validator) builderTargetsForKey(pk pubkey) []builderTarget { } targets = append(targets, t) } - for _, relay := range bc.Relays { - targets = append(targets, builderTarget{url: relay, maxPayment: fbMax, minBid: fbMin, boostFactor: fbBoost}) - } return targets } @@ -1494,6 +1494,9 @@ func (v *validator) submitBuilderPreferenceRequests(ctx context.Context, reqs [] // inline on the block request for pk's proposal at slot, pairing each cached // signed auth with its resolved caps. func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Slot) []*ethpb.BuilderPreferenceV1 { + if slots.ToEpoch(slot) < params.BeaconConfig().GloasForkEpoch { + return nil + } targets := v.builderTargetsForKey(pk) prefs := make([]*ethpb.BuilderPreferenceV1, 0, len(targets)) seen := make(map[string]bool, len(targets)) @@ -1513,6 +1516,7 @@ func (v *validator) buildBuilderPreferencesForSlot(pk pubkey, slot primitives.Sl Auth: auth, }, Url: t.url, + Pubkey: t.pubkey, BuilderBoostFactor: t.boostFactor, } if t.minBid != nil { @@ -1587,8 +1591,13 @@ func (v *validator) buildSignedRegReqs( return signedValRegRequests } + isV2 := v.ProposerSettings().Version == proposer.SchemaV2 if v.ProposerSettings().DefaultConfig != nil && v.ProposerSettings().DefaultConfig.FeeRecipientConfig == nil && v.ProposerSettings().DefaultConfig.BuilderConfig != nil { - log.Warn("Default builder config has no default fee recipient; only keys with their own fee recipient can register") + if isV2 { + log.Warn("Default builder config has no default fee recipient; only keys with their own fee recipient can register") + } else { + log.Warn("Builder is `enabled` in default config but will be ignored because no fee recipient was provided!") + } } for i, k := range activePubkeys { @@ -1598,38 +1607,63 @@ func (v *validator) buildSignedRegReqs( continue } + var enabled bool feeRecipient := common.HexToAddress(params.BeaconConfig().EthBurnAddressHex) - hasFeeRecipient := false - var defBC, perKeyBC *proposer.BuilderConfig + gasLimit := params.BeaconConfig().DefaultBuilderGasLimit - if v.ProposerSettings().DefaultConfig != nil { - defaultConfig := v.ProposerSettings().DefaultConfig - defBC = defaultConfig.BuilderConfig - if defaultConfig.FeeRecipientConfig != nil { + if isV2 { + hasFeeRecipient := false + var defBC, perKeyBC *proposer.BuilderConfig + if defaultConfig := v.ProposerSettings().DefaultConfig; defaultConfig != nil { + defBC = defaultConfig.BuilderConfig + if defaultConfig.FeeRecipientConfig != nil { + feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient + hasFeeRecipient = true + } + } + if v.ProposerSettings().ProposeConfig != nil { + if config, ok := v.ProposerSettings().ProposeConfig[k]; ok && config != nil { + perKeyBC = config.BuilderConfig + if config.FeeRecipientConfig != nil { + feeRecipient = config.FeeRecipientConfig.FeeRecipient + hasFeeRecipient = true + } + } + } + // Field-level resolution: per-key builder fields inherit from the default; + // registration still requires a fee recipient configured at some level. + bc := proposer.EffectiveBuilderConfig(perKeyBC, defBC) + enabled = bc.IsEnabled() && hasFeeRecipient + if bc.GasLimit != 0 { + gasLimit = uint64(bc.GasLimit) + } + } else { + // V1 settings keep the pre-v2 object-level semantics so existing + // registrations are unchanged; inheritance starts at SchemaV2. + if defaultConfig := v.ProposerSettings().DefaultConfig; defaultConfig != nil && defaultConfig.FeeRecipientConfig != nil { feeRecipient = defaultConfig.FeeRecipientConfig.FeeRecipient - hasFeeRecipient = true + if defaultConfig.BuilderConfig.IsEnabled() { + gasLimit = uint64(defaultConfig.BuilderConfig.GasLimit) + enabled = true + } } - } - if v.ProposerSettings().ProposeConfig != nil { - if config, ok := v.ProposerSettings().ProposeConfig[k]; ok && config != nil { - perKeyBC = config.BuilderConfig - if config.FeeRecipientConfig != nil { + if v.ProposerSettings().ProposeConfig != nil { + if config, ok := v.ProposerSettings().ProposeConfig[k]; ok && config != nil && config.FeeRecipientConfig != nil { feeRecipient = config.FeeRecipientConfig.FeeRecipient - hasFeeRecipient = true + if bc := config.BuilderConfig; bc != nil { + if bc.IsEnabled() { + gasLimit = uint64(bc.GasLimit) + enabled = true + } else { + enabled = false + } + } } } } - - // Field-level resolution: per-key builder fields inherit from the default; - // registration still requires a fee recipient configured at some level. - bc := proposer.EffectiveBuilderConfig(perKeyBC, defBC) - if !bc.IsEnabled() || !hasFeeRecipient { + if !enabled { continue } - gasLimit := params.BeaconConfig().DefaultBuilderGasLimit - if bc.GasLimit != 0 { - gasLimit = uint64(bc.GasLimit) - } req := ðpb.ValidatorRegistrationV1{ FeeRecipient: feeRecipient[:], diff --git a/validator/client/validator_test.go b/validator/client/validator_test.go index 5d7758cc942d..c1757b3ea901 100644 --- a/validator/client/validator_test.go +++ b/validator/client/validator_test.go @@ -3332,16 +3332,55 @@ func TestValidator_buildSignedRegReqs_DefaultConfigDisabled(t *testing.T) { } actual := v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false) - assert.Equal(t, 2, len(actual)) + assert.Equal(t, 1, len(actual)) assert.DeepEqual(t, feeRecipient1[:], actual[0].Message.FeeRecipient) assert.Equal(t, uint64(1111), actual[0].Message.GasLimit) assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey) +} - // Field-level inheritance: pubkey3's enabled builder registers with its own - // gas limit and the inherited default fee recipient. - assert.DeepEqual(t, defaultFeeRecipient[:], actual[1].Message.FeeRecipient) - assert.Equal(t, uint64(3333), actual[1].Message.GasLimit) - assert.DeepEqual(t, pubkey3[:], actual[1].Message.Pubkey) +// The same settings shape registers differently by schema version: v1 keeps the +// legacy object-level semantics, v2 applies field-level inheritance. +func TestValidator_buildSignedRegReqs_VersionGatesInheritance(t *testing.T) { + pubkey1 := pubkeyFromString(t, "0x111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") + defaultFeeRecipient := feeRecipientFromString(t, "0xdddddddddddddddddddddddddddddddddddddddd") + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := t.Context() + + signature := blsmock.NewMockSignature(ctrl) + signature.EXPECT().Marshal().Return([]byte{}).AnyTimes() + signer := func(_ context.Context, _ *validatorpb.SignRequest) (bls.Signature, error) { + return signature, nil + } + + newSettings := func(version uint32) *proposer.Settings { + return &proposer.Settings{ + Version: version, + DefaultConfig: &proposer.Option{ + FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: defaultFeeRecipient}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 9999}, + }, + ProposeConfig: map[[48]byte]*proposer.Option{ + // Per-key disable without a per-key fee recipient. + pubkey1: {BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(false)}}, + }, + } + } + v := validator{ + signedValidatorRegistrations: map[[48]byte]*ethpb.SignedValidatorRegistrationV1{}, + pubkeyToStatus: map[[48]byte]*validatorStatus{ + pubkey1: {publicKey: pubkey1[:], status: ðpb.ValidatorStatusResponse{Status: ethpb.ValidatorStatus_ACTIVE}}, + }, + } + pubkeys := [][fieldparams.BLSPubkeyLength]byte{pubkey1} + + v.proposerSettings = newSettings(0) + assert.Equal(t, 1, len(v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false))) + + v.proposerSettings = newSettings(proposer.SchemaV2) + v.signedValidatorRegistrations = map[[48]byte]*ethpb.SignedValidatorRegistrationV1{} + assert.Equal(t, 0, len(v.buildSignedRegReqs(ctx, pubkeys, signer, 0, false))) } func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { @@ -3443,8 +3482,7 @@ func TestValidator_buildSignedRegReqs_DefaultConfigEnabled(t *testing.T) { assert.DeepEqual(t, pubkey1[:], actual[0].Message.Pubkey) assert.DeepEqual(t, defaultFeeRecipient[:], actual[1].Message.FeeRecipient) - // Field-level inheritance: pubkey3's explicit per-key gas limit wins over the default's. - assert.Equal(t, uint64(3333), actual[1].Message.GasLimit) + assert.Equal(t, uint64(9999), actual[1].Message.GasLimit) assert.DeepEqual(t, pubkey3[:], actual[1].Message.Pubkey) t.Run("mid epoch only pushes newly added key", func(t *testing.T) { diff --git a/validator/db/convert_test.go b/validator/db/convert_test.go index 6c53fbb5700d..670f649e1d43 100644 --- a/validator/db/convert_test.go +++ b/validator/db/convert_test.go @@ -99,7 +99,6 @@ func TestDB_ConvertDatabase(t *testing.T) { require.NoError(t, err, "could not save graffiti ordered index") // Save the proposer settings. - var relays []string = nil expectedProposerSettings := &proposer.Settings{} if withProposerSettings { @@ -112,7 +111,6 @@ func TestDB_ConvertDatabase(t *testing.T) { BuilderConfig: &proposer.BuilderConfig{ Enabled: proto.Bool(true), GasLimit: 42, - Relays: relays, }, }, }, @@ -123,7 +121,6 @@ func TestDB_ConvertDatabase(t *testing.T) { BuilderConfig: &proposer.BuilderConfig{ Enabled: proto.Bool(false), GasLimit: 43, - Relays: relays, }, }, } diff --git a/validator/db/filesystem/db.go b/validator/db/filesystem/db.go index 1ec43f30a282..9eac85b02ac4 100644 --- a/validator/db/filesystem/db.go +++ b/validator/db/filesystem/db.go @@ -239,23 +239,6 @@ func (s *Store) configuration() (*Configuration, error) { return nil, errors.Wrapf(err, "could not unmarshal %s", cleanedConfigFilePath) } - // yaml.Unmarshal converts nil array to empty array. - // To get the same behavior as the BoltDB implementation, we need to convert empty array to nil. - if config.ProposerSettings != nil && - config.ProposerSettings.DefaultConfig != nil && - config.ProposerSettings.DefaultConfig.Builder != nil && - len(config.ProposerSettings.DefaultConfig.Builder.Relays) == 0 { - config.ProposerSettings.DefaultConfig.Builder.Relays = nil - } - - if config.ProposerSettings != nil && config.ProposerSettings.ProposerConfig != nil { - for _, option := range config.ProposerSettings.ProposerConfig { - if option.Builder != nil && len(option.Builder.Relays) == 0 { - option.Builder.Relays = nil - } - } - } - return config, nil } diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index cfd1290de935..c5644c4ec550 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -272,11 +272,16 @@ func builderConfigFromJSON(in *BuilderConfigJson) (*proposer.BuilderConfig, erro } bc.BuilderBoostFactor = &v } + seen := make(map[string]bool, len(in.Builders)) for i, entry := range in.Builders { be, err := builderEntryFromJSON(entry, i) if err != nil { return nil, err } + if seen[be.URL] { + return nil, errors.Errorf("builder.builders[%d]: two builder entries share the same url", i) + } + seen[be.URL] = true bc.Builders = append(bc.Builders, be) } return bc, nil diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index ac560e2238a5..0ec9554882ba 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -270,6 +270,22 @@ func TestServer_SetGetValidatorConfig_MaxExecutionPaymentZeroVsAbsent(t *testing assert.Equal(t, (*string)(nil), absent.Builder.MaxExecutionPayment) } +func TestServer_SetValidatorConfig_DuplicateBuilderURL(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + + body := `{"configs":{"` + pk + `":{"builder":{"enabled":true,"builders":[{"url":"https://b.example"},{"url":"https://b.example","min_bid":"5"}]}}}}` + w := postValidatorConfig(t, srv, body) + require.Equal(t, http.StatusOK, w.Code) + setResp := &SetValidatorConfigResponse{} + require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) + require.Equal(t, configStatusError, setResp.Data[pk].Status) + assert.StringContains(t, "share the same url", setResp.Data[pk].Message) + + getResp := getValidatorConfig(t, srv, "") + require.Equal(t, 0, len(getResp.Data.Configs)) +} + func TestServer_SetValidatorConfig_InvalidBuilderURL(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) From c256348536257d1bfa760d303971246fa9af1673 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Wed, 22 Jul 2026 11:56:27 -0500 Subject: [PATCH 15/19] fixing issues around enable builder, api endpoint and making sure empty builder are identical and unset across validator backends --- .../james-prysm_validator-config-endpoint.md | 3 + config/proposer/BUILD.bazel | 1 + config/proposer/loader/loader.go | 19 ++++- config/proposer/loader/loader_test.go | 78 +++++++++++++++++++ .../testdata/good-v2-proposer-config.json | 25 ++++++ config/proposer/settings.go | 5 +- validator/db/filesystem/BUILD.bazel | 2 + .../db/filesystem/proposer_settings_test.go | 39 ++++++++++ validator/rpc/handlers_validator_config.go | 2 + .../rpc/handlers_validator_config_test.go | 22 ++++++ 10 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 config/proposer/loader/testdata/good-v2-proposer-config.json diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index 510a3d816c18..a5ffcb733f13 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -11,12 +11,15 @@ - Send builder preferences inline with the block request instead of caching them on the beacon node; request auths sign opaque `auth_data` per builder-specs #165, with the builder URL carried separately. - Merge file- or URL-loaded proposer settings per key: the file only overrides keys it names, and the settings schema version never regresses. - Reject duplicate builder urls when setting the validator config; previously persisted duplicates keep the first entry for each url. +- For v2 settings, `--enable-builder` sets the default builder toggle only when it is unset; an explicit `enabled` and per-key entries are left to field-level inheritance. - Enforce the per-builder `pubkey` from the validator config during bid selection: when set, only bids from that builder key are accepted for the entry's url. ### Fixed - Normalize legacy proposer settings on load so unset builder `enabled` stays disabled and both validator DB backends decode `max_execution_payment` identically. +- Report the gas limit for v1 proposer settings via `GET /eth/v1/validator/config` instead of omitting it. +- Decode empty builder `auth_data`/`pubkey` identically to unset across both validator DB backends. ### Removed diff --git a/config/proposer/BUILD.bazel b/config/proposer/BUILD.bazel index 504079e45527..7fe6d79d0076 100644 --- a/config/proposer/BUILD.bazel +++ b/config/proposer/BUILD.bazel @@ -34,6 +34,7 @@ go_test( "//config/params:go_default_library", "//consensus-types/validator:go_default_library", "//encoding/bytesutil:go_default_library", + "//proto/prysm/v1alpha1/validator-client:go_default_library", "//testing/assert:go_default_library", "//testing/require:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", diff --git a/config/proposer/loader/loader.go b/config/proposer/loader/loader.go index cb9cf5e0040f..754806b59947 100644 --- a/config/proposer/loader/loader.go +++ b/config/proposer/loader/loader.go @@ -290,7 +290,7 @@ func mergeProposerSettings(loaded, db *validatorpb.ProposerSettingsPayload, opti } if merged.Version == proposer.SchemaV2 { - return mergeProposerSettingsV2(merged, loaded, db, gasLimitOnly) + return mergeProposerSettingsV2(merged, loaded, db, builderConfig, gasLimitOnly) } return mergeProposerSettingsV1(merged, loaded, db, builderConfig, gasLimitOnly) } @@ -382,7 +382,7 @@ func mergeProposerSettingsV1(merged, loaded, db *validatorpb.ProposerSettingsPay return merged } -func mergeProposerSettingsV2(merged, loaded, db *validatorpb.ProposerSettingsPayload, gasLimitOnly *validator.Uint64) *validatorpb.ProposerSettingsPayload { +func mergeProposerSettingsV2(merged, loaded, db *validatorpb.ProposerSettingsPayload, builderConfig *validatorpb.BuilderConfig, gasLimitOnly *validator.Uint64) *validatorpb.ProposerSettingsPayload { if db != nil && db.DefaultConfig != nil { merged.DefaultConfig = db.DefaultConfig } @@ -391,6 +391,21 @@ func mergeProposerSettingsV2(merged, loaded, db *validatorpb.ProposerSettingsPay } merged.ProposerConfig = overlayProposerConfig(db, loaded) + // --enable-builder fills in only the default builder toggle when unset; an + // explicit enabled and every per-key entry are left for field-level inheritance. + if builderConfig != nil { + if merged.DefaultConfig == nil { + merged.DefaultConfig = &validatorpb.ProposerOptionPayload{} + } + if merged.DefaultConfig.Builder == nil { + merged.DefaultConfig.Builder = &validatorpb.BuilderConfig{} + } + if merged.DefaultConfig.Builder.Enabled == nil { + enabled := true + merged.DefaultConfig.Builder.Enabled = &enabled + } + } + if gasLimitOnly == nil { return merged } diff --git a/config/proposer/loader/loader_test.go b/config/proposer/loader/loader_test.go index a25f2114b580..0897829ad876 100644 --- a/config/proposer/loader/loader_test.go +++ b/config/proposer/loader/loader_test.go @@ -406,6 +406,46 @@ func TestProposerSettingsLoader(t *testing.T) { }, wantErr: "", }, + { + name: "v2 file with builders list loads at v2 and dedups duplicate builder urls", + args: args{ + proposerSettingsFlagValues: &proposerSettingsFlag{ + dir: "./testdata/good-v2-proposer-config.json", + }, + }, + want: func() *proposer.Settings { + key1, err := hexutil.Decode("0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a") + require.NoError(t, err) + u64 := func(v uint64) *validator.Uint64 { u := validator.Uint64(v); return &u } + return &proposer.Settings{ + Version: proposer.SchemaV2, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + bytesutil.ToBytes48(key1): { + FeeRecipientConfig: &proposer.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3"), + }, + GasLimit: 40000000, + BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + MinBid: u64(500000000), + Builders: []*proposer.BuilderEntry{ + {URL: "https://builder-a.example", MaxExecutionPayment: u64(1000000000)}, + {URL: "https://builder-b.example"}, + }, + }, + }, + }, + DefaultConfig: &proposer.Option{ + FeeRecipientConfig: &proposer.FeeRecipientConfig{ + FeeRecipient: common.HexToAddress("0x6e35733c5af9B61374A128e6F85f553aF09ff89A"), + }, + GasLimit: 30000000, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(false)}, + }, + } + }, + wantErr: "", + }, { name: "Happy Path Suggested Fee ", args: args{ @@ -1305,6 +1345,44 @@ func Test_mergeProposerSettings_VersionGatesBuilderReset(t *testing.T) { require.NotNil(t, merged.DefaultConfig.Builder) require.Equal(t, validator.Uint64(40000000), merged.DefaultConfig.Builder.GasLimit) }) + t.Run("v2 --enable-builder fills the default toggle only when unset", func(t *testing.T) { + enabled := true + opts := &flagOptions{builderConfig: &proposer.BuilderConfig{Enabled: &enabled}} + + // No default builder: the flag creates it enabled. + db := &validatorpb.ProposerSettingsPayload{ + Version: proposer.SchemaV2, + DefaultConfig: &validatorpb.ProposerOptionPayload{FeeRecipient: "0x"}, + } + merged := mergeProposerSettings(nil, db, opts) + require.NotNil(t, merged.DefaultConfig.Builder) + require.NotNil(t, merged.DefaultConfig.Builder.Enabled) + require.Equal(t, true, *merged.DefaultConfig.Builder.Enabled) + + // An explicit default enabled=false is NOT overridden by the flag. + disabled := false + db2 := &validatorpb.ProposerSettingsPayload{ + Version: proposer.SchemaV2, + DefaultConfig: &validatorpb.ProposerOptionPayload{ + FeeRecipient: "0x", + Builder: &validatorpb.BuilderConfig{Enabled: &disabled}, + }, + } + merged2 := mergeProposerSettings(nil, db2, opts) + require.NotNil(t, merged2.DefaultConfig.Builder.Enabled) + require.Equal(t, false, *merged2.DefaultConfig.Builder.Enabled) + + // Per-key entries are untouched by the flag. + db3 := &validatorpb.ProposerSettingsPayload{ + Version: proposer.SchemaV2, + DefaultConfig: &validatorpb.ProposerOptionPayload{FeeRecipient: "0x"}, + ProposerConfig: map[string]*validatorpb.ProposerOptionPayload{ + "0xkey": {FeeRecipient: "0xk"}, + }, + } + merged3 := mergeProposerSettings(nil, db3, opts) + require.IsNil(t, merged3.ProposerConfig["0xkey"].Builder) + }) } func Test_mergeProposerSettings_V2LoadedOverridesDB(t *testing.T) { diff --git a/config/proposer/loader/testdata/good-v2-proposer-config.json b/config/proposer/loader/testdata/good-v2-proposer-config.json new file mode 100644 index 000000000000..06f9c133f5d9 --- /dev/null +++ b/config/proposer/loader/testdata/good-v2-proposer-config.json @@ -0,0 +1,25 @@ +{ + "proposer_config": { + "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a": { + "fee_recipient": "0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3", + "gas_limit": "40000000", + "builder": { + "enabled": true, + "min_bid": "500000000", + "builders": [ + { "url": "https://builder-a.example", "max_execution_payment": "1000000000" }, + { "url": "https://builder-b.example" }, + { "url": "https://builder-a.example", "max_execution_payment": "2" } + ] + } + } + }, + "default_config": { + "fee_recipient": "0x6e35733c5af9B61374A128e6F85f553aF09ff89A", + "gas_limit": "30000000", + "builder": { + "enabled": false + } + }, + "version": 2 +} diff --git a/config/proposer/settings.go b/config/proposer/settings.go index b040d7392412..14c77f6f5e24 100644 --- a/config/proposer/settings.go +++ b/config/proposer/settings.go @@ -244,10 +244,11 @@ func builderEntryFromConsensus(from *validatorpb.BuilderEntry) *BuilderEntry { MaxExecutionPayment: from.MaxExecutionPayment, BuilderBoostFactor: from.BuilderBoostFactor, } - if from.Pubkey != nil { + // Treat empty as absent so bolt (nil) and filesystem (empty) round-trips agree. + if len(from.Pubkey) != 0 { e.Pubkey = bytesutil.SafeCopyBytes(from.Pubkey) } - if from.AuthData != nil { + if len(from.AuthData) != 0 { e.AuthData = bytesutil.SafeCopyBytes(from.AuthData) } return e diff --git a/validator/db/filesystem/BUILD.bazel b/validator/db/filesystem/BUILD.bazel index e09134c5dca6..798db1eeea80 100644 --- a/validator/db/filesystem/BUILD.bazel +++ b/validator/db/filesystem/BUILD.bazel @@ -55,6 +55,7 @@ go_test( "//config/proposer:go_default_library", "//consensus-types/blocks:go_default_library", "//consensus-types/primitives:go_default_library", + "//consensus-types/validator:go_default_library", "//crypto/bls:go_default_library", "//io/file:go_default_library", "//proto/prysm/v1alpha1:go_default_library", @@ -67,5 +68,6 @@ go_test( "//validator/testing:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", + "@org_golang_google_protobuf//proto:go_default_library", ], ) diff --git a/validator/db/filesystem/proposer_settings_test.go b/validator/db/filesystem/proposer_settings_test.go index 1e3de64924ac..f1dce8e7e47f 100644 --- a/validator/db/filesystem/proposer_settings_test.go +++ b/validator/db/filesystem/proposer_settings_test.go @@ -5,9 +5,12 @@ import ( fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/proposer" + "github.com/OffchainLabs/prysm/v7/consensus-types/validator" validatorpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1/validator-client" "github.com/OffchainLabs/prysm/v7/testing/require" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "google.golang.org/protobuf/proto" ) func getPubkeyFromString(t *testing.T, pubkeyString string) [fieldparams.BLSPubkeyLength]byte { @@ -226,3 +229,39 @@ func TestStore_SaveProposerSettings(t *testing.T) { }) } } + +// v2 settings with a builders list must survive a filesystem round-trip: Version +// preserved and optional entry bytes normalized to nil (matching the bolt backend). +func TestStore_ProposerSettings_V2BuildersRoundTrip(t *testing.T) { + ctx := t.Context() + u64 := func(v uint64) *validator.Uint64 { u := validator.Uint64(v); return &u } + key := getPubkeyFromString(t, "0xa057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7a") + + in := &proposer.Settings{ + Version: proposer.SchemaV2, + ProposeConfig: map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option{ + key: { + FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress("0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3")}, + GasLimit: 40000000, + BuilderConfig: &proposer.BuilderConfig{ + Enabled: proto.Bool(true), + Builders: []*proposer.BuilderEntry{ + {URL: "https://builder-a.example", AuthData: []byte("secret"), MaxExecutionPayment: u64(1000000000)}, + {URL: "https://builder-b.example"}, + }, + }, + }, + }, + } + + store, err := NewStore(t.TempDir(), nil) + require.NoError(t, err) + require.NoError(t, store.SaveProposerSettings(ctx, in)) + + got, err := store.ProposerSettings(ctx) + require.NoError(t, err) + require.DeepEqual(t, in, got) + + // The entry without auth_data must load back as nil, not empty-non-nil. + require.IsNil(t, got.ProposeConfig[key].BuilderConfig.Builders[1].AuthData) +} diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index c5644c4ec550..2d691f681d52 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -177,6 +177,8 @@ func validatorConfigFromOption(opt *proposer.Option) *ValidatorConfig { } if opt.GasLimit != 0 { cfg.TargetGasLimit = strPtr(formatUint(opt.GasLimit)) + } else if opt.BuilderConfig != nil && opt.BuilderConfig.GasLimit != 0 { + cfg.TargetGasLimit = strPtr(formatUint(opt.BuilderConfig.GasLimit)) } if opt.GraffitiConfig != nil { g := opt.GraffitiConfig.Graffiti diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index 0ec9554882ba..859098e0f39d 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -14,6 +14,7 @@ import ( mocks "github.com/OffchainLabs/prysm/v7/validator/testing" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" + "google.golang.org/protobuf/proto" ) // setupConfigServer builds a Server with a derived keymanager holding numKeys @@ -270,6 +271,27 @@ func TestServer_SetGetValidatorConfig_MaxExecutionPaymentZeroVsAbsent(t *testing assert.Equal(t, (*string)(nil), absent.Builder.MaxExecutionPayment) } +// v1 settings keep the gas limit on the builder config; GET must still surface it +// as target_gas_limit rather than dropping it. +func TestServer_GetValidatorConfig_V1BuilderGasLimit(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + require.NoError(t, srv.validatorService.SetProposerSettings(t.Context(), &proposer.Settings{ + Version: proposer.SchemaV1, + ProposeConfig: map[[48]byte]*proposer.Option{ + keys[0]: { + FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress("0xabcf8e0d4e9587369b2301d0790347320302cc09")}, + BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 40000000}, + }, + }, + })) + + pk := hexutil.Encode(keys[0][:]) + cfg := getValidatorConfig(t, srv, "").Data.Configs[pk] + require.NotNil(t, cfg) + require.NotNil(t, cfg.TargetGasLimit) + require.Equal(t, "40000000", *cfg.TargetGasLimit) +} + func TestServer_SetValidatorConfig_DuplicateBuilderURL(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) From bce03d7325069e87967b521ac5176346facfe99e Mon Sep 17 00:00:00 2001 From: james-prysm Date: Fri, 24 Jul 2026 11:52:44 -0500 Subject: [PATCH 16/19] wip --- api/client/builder/client.go | 4 +- api/client/builder/types_test.go | 18 ++ api/rest/rest_handler_test.go | 15 + beacon-chain/rpc/eth/beacon/handlers_test.go | 22 ++ .../rpc/eth/validator/builder_preferences.go | 58 +++- .../eth/validator/builder_preferences_test.go | 53 ++++ .../validator/handlers_block_gloas_test.go | 29 ++ .../v1alpha1/validator/builder_preferences.go | 4 +- .../validator/builder_preferences_test.go | 7 +- .../james-prysm_validator-config-endpoint.md | 4 + proto/prysm/v1alpha1/BUILD.bazel | 2 + proto/prysm/v1alpha1/gloas_builder_api.go | 69 ++++ proto/prysm/v1alpha1/gloas_builder_api.pb.go | 175 ++++++++++- proto/prysm/v1alpha1/gloas_builder_api.proto | 18 ++ proto/prysm/v1alpha1/gloas_builder_api.ssz.go | 294 ++++++++++++++++++ .../prysm/v1alpha1/gloas_builder_api_test.go | 53 ++++ .../client/beacon-api/builder_preferences.go | 35 +-- .../client/beacon-api/get_beacon_block.go | 5 +- .../client/grpc-api/grpc_validator_client.go | 8 +- .../grpc-api/grpc_validator_client_test.go | 16 + validator/client/propose_test.go | 30 ++ 21 files changed, 860 insertions(+), 59 deletions(-) create mode 100644 proto/prysm/v1alpha1/gloas_builder_api.go create mode 100644 proto/prysm/v1alpha1/gloas_builder_api_test.go diff --git a/api/client/builder/client.go b/api/client/builder/client.go index e57249e2bf3e..592f41416e3e 100644 --- a/api/client/builder/client.go +++ b/api/client/builder/client.go @@ -227,7 +227,7 @@ func (c *Client) doWithStatus(ctx context.Context, method string, path string, b u := c.baseURL.ResolveReference(&url.URL{Path: path}) - span.SetAttributes(trace.StringAttribute("url", u.Redacted()), + span.SetAttributes(trace.StringAttribute("url", logs.MaskCredentialsLogging(u.String())), trace.StringAttribute("method", method)) req, err := http.NewRequestWithContext(ctx, method, u.String(), body) @@ -794,7 +794,7 @@ func unexpectedStatusErr(response *http.Response, expected []int) error { } else { body = "response body:\n" + string(bodyBytes) } - msg := fmt.Sprintf("expected=%v, got=%d, url=%s, body=%s", expected, response.StatusCode, response.Request.URL.Redacted(), body) + msg := fmt.Sprintf("expected=%v, got=%d, url=%s, body=%s", expected, response.StatusCode, logs.MaskCredentialsLogging(response.Request.URL.String()), body) var sentinel error switch response.StatusCode { diff --git a/api/client/builder/types_test.go b/api/client/builder/types_test.go index cf613b79ab47..f492bd9ab37b 100644 --- a/api/client/builder/types_test.go +++ b/api/client/builder/types_test.go @@ -9,6 +9,7 @@ import ( "net/http" "net/url" "os" + "strings" "testing" "github.com/OffchainLabs/go-bitfield" @@ -1797,6 +1798,23 @@ func TestErrorMessage_unexpectedStatusErr(t *testing.T) { } } +// A builder URL carrying credentials as userinfo or a query token must never leak +// into the error message; MaskCredentialsLogging masks both (url.Redacted did not). +func TestUnexpectedStatusErr_RedactsCredentials(t *testing.T) { + const secret = "supersecrettoken" + req := &http.Request{URL: &url.URL{ + Scheme: "https", + User: url.User(secret), + Host: "builder.example", + Path: "/eth/v1/builder/header", + RawQuery: "api_key=" + secret, + }} + resp := &http.Response{Request: req, StatusCode: http.StatusBadGateway, Body: io.NopCloser(bytes.NewReader([]byte("upstream down")))} + err := unexpectedStatusErr(resp, []int{http.StatusOK}) + require.NotNil(t, err) + require.Equal(t, false, strings.Contains(err.Error(), secret), "error leaked credentials: %s", err.Error()) +} + // isSSZRejection keys off the HTTP status, independent of the error body format, so a builder // returning plain text (Commit Boost) is detected the same as a spec-compliant JSON body. func TestUnexpectedStatusErr_SSZRejectionByStatus(t *testing.T) { diff --git a/api/rest/rest_handler_test.go b/api/rest/rest_handler_test.go index a67fc27f36fa..aab27e694ad6 100644 --- a/api/rest/rest_handler_test.go +++ b/api/rest/rest_handler_test.go @@ -48,6 +48,21 @@ func TestPostSSZ_NonJSONErrorBodyIsTyped(t *testing.T) { require.Equal(t, http.StatusUnsupportedMediaType, errJson.Code) } +// PostSSZ always sends an SSZ (octet-stream) request body. +func TestPostSSZ_ContentType(t *testing.T) { + var got string + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + got = r.Header.Get("Content-Type") + w.WriteHeader(http.StatusOK) + })) + defer srv.Close() + c := NewHandler(http.Client{}, srv.URL) + + _, _, err := c.PostSSZ(context.Background(), "/eth/v1/test", nil, bytes.NewBuffer([]byte{0x01})) + require.NoError(t, err) + require.Equal(t, api.OctetStreamMediaType, got) +} + func TestGetSSZ_NonJSONErrorBodyIsTyped(t *testing.T) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { http.Error(w, "Not Acceptable", http.StatusNotAcceptable) diff --git a/beacon-chain/rpc/eth/beacon/handlers_test.go b/beacon-chain/rpc/eth/beacon/handlers_test.go index 79458da76a16..a6f1e0fe9ae1 100644 --- a/beacon-chain/rpc/eth/beacon/handlers_test.go +++ b/beacon-chain/rpc/eth/beacon/handlers_test.go @@ -1529,6 +1529,28 @@ func TestVersionHeaderFromRequest(t *testing.T) { }) } +// The winning builder url arrives as the Eth-Builder-Url request header on publish +// and must be stamped onto the block forwarded to the v1alpha1 proposer. +func TestPublishBlock_StampsBuilderUrlHeader(t *testing.T) { + ctrl := gomock.NewController(t) + const url = "https://builder.example" + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().ProposeBeaconBlock(gomock.Any(), mock.MatchedBy(func(req *eth.GenericSignedBeaconBlock) bool { + return req.BuilderUrl == url + })) + server := &Server{ + V1Alpha1ValidatorServer: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + } + request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.Phase0Block))) + request.Header.Set(api.VersionHeader, version.String(version.Phase0)) + request.Header.Set(api.EthBuilderUrlHeader, url) + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + server.PublishBlockV2(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) +} + func TestPublishBlockV2(t *testing.T) { ctrl := gomock.NewController(t) t.Run("Phase 0", func(t *testing.T) { diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index 07547a050563..64078f885952 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -5,7 +5,9 @@ import ( "io" "net/http" "strconv" + "strings" + "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server" "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/shared" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" @@ -40,12 +42,20 @@ type requestAuthJson struct { Slot string `json:"slot"` } -// parseBuilderPreferencesBody decodes the JIT produce-block POST body. Malformed -// entries are ignored per the spec; an empty body yields no preferences. +// parseBuilderPreferencesBody decodes the JIT produce-block POST body, accepting +// SSZ (octet-stream) or JSON. Malformed entries are ignored per the spec; an empty +// body yields no preferences. func parseBuilderPreferencesBody(r *http.Request) ([]*eth.BuilderPreferenceV1, error) { if r.Body == nil { return nil, nil } + if strings.Contains(r.Header.Get("Content-Type"), api.OctetStreamMediaType) { + return parseBuilderPreferencesSSZ(r) + } + return parseBuilderPreferencesJSON(r) +} + +func parseBuilderPreferencesJSON(r *http.Request) ([]*eth.BuilderPreferenceV1, error) { var entries []*builderEntryJson if err := json.NewDecoder(r.Body).Decode(&entries); err != nil { if errors.Is(err, io.EOF) { @@ -53,20 +63,52 @@ func parseBuilderPreferencesBody(r *http.Request) ([]*eth.BuilderPreferenceV1, e } return nil, errors.Wrap(err, "could not decode builder preferences") } - out := make([]*eth.BuilderPreferenceV1, 0, len(entries)) - seen := make(map[string]bool, len(entries)) + prefs := make([]*eth.BuilderPreferenceV1, 0, len(entries)) for _, p := range entries { conv, err := p.toConsensus() if err != nil { log.WithError(err).Debug("Ignoring malformed builder entry") continue } - // Duplicate urls invalidate the whole request, unlike malformed entries. - if seen[conv.Url] { + prefs = append(prefs, conv) + } + return dedupeBuilderPreferences(prefs) +} + +func parseBuilderPreferencesSSZ(r *http.Request) ([]*eth.BuilderPreferenceV1, error) { + body, err := io.ReadAll(r.Body) + if err != nil { + return nil, errors.Wrap(err, "could not read builder preferences") + } + if len(body) == 0 { + return nil, nil + } + list := ð.ProduceBuilderEntryListV1{} + if err := list.UnmarshalSSZ(body); err != nil { + return nil, errors.Wrap(err, "could not decode builder preferences") + } + prefs := make([]*eth.BuilderPreferenceV1, 0) + for _, p := range eth.BuilderPreferencesFromSSZ(list) { + if p.Url == "" || p.Request == nil || p.Request.Auth == nil || p.Request.Auth.Message == nil { + log.Debug("Ignoring malformed builder entry") + continue + } + prefs = append(prefs, p) + } + return dedupeBuilderPreferences(prefs) +} + +// dedupeBuilderPreferences enforces the unique-url rule: duplicates invalidate the +// whole request, unlike malformed entries which were already skipped. +func dedupeBuilderPreferences(prefs []*eth.BuilderPreferenceV1) ([]*eth.BuilderPreferenceV1, error) { + out := make([]*eth.BuilderPreferenceV1, 0, len(prefs)) + seen := make(map[string]bool, len(prefs)) + for _, p := range prefs { + if seen[p.Url] { return nil, errors.Errorf("two builder entries share the same url") } - seen[conv.Url] = true - out = append(out, conv) + seen[p.Url] = true + out = append(out, p) } return out, nil } diff --git a/beacon-chain/rpc/eth/validator/builder_preferences_test.go b/beacon-chain/rpc/eth/validator/builder_preferences_test.go index 1f39eff458f7..3e0e4f1e39b4 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences_test.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences_test.go @@ -7,8 +7,10 @@ import ( "net/http/httptest" "testing" + "github.com/OffchainLabs/prysm/v7/api" blockchainTesting "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing" mockSync "github.com/OffchainLabs/prysm/v7/beacon-chain/sync/initial-sync/testing" + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1" "github.com/OffchainLabs/prysm/v7/testing/assert" mock2 "github.com/OffchainLabs/prysm/v7/testing/mock" @@ -81,6 +83,57 @@ func TestParseBuilderPreferencesBody_DuplicateURL(t *testing.T) { require.ErrorContains(t, "share the same url", err) } +// An octet-stream body is decoded as SSZ, and the sentinels round-trip back to the +// same effective preferences the JSON path produces. +func TestParseBuilderPreferencesBody_SSZ(t *testing.T) { + boost := uint64(200) + minBid := primitives.Gwei(5) + pubkey := make([]byte, 48) + pubkey[0] = 0xAB + prefs := []*eth.BuilderPreferenceV1{{ + Url: "https://b.example", + Request: ð.BuilderPreferencesRequestV1{ + Preferences: ð.BuilderPreferencesV1{MaxExecutionPayment: 1000}, + Auth: ð.SignedRequestAuthV1{Message: ð.RequestAuthV1{Data: []byte{1}, Slot: 7}, Signature: make([]byte, 96)}, + }, + MinBid: &minBid, + BuilderBoostFactor: &boost, + Pubkey: pubkey, + }} + body, err := eth.BuilderPreferencesToSSZ(prefs).MarshalSSZ() + require.NoError(t, err) + + r := httptest.NewRequest("POST", "/", bytes.NewBuffer(body)) + r.Header.Set("Content-Type", api.OctetStreamMediaType) + got, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 1, len(got)) + require.Equal(t, "https://b.example", got[0].Url) + require.Equal(t, primitives.Gwei(5), got[0].GetMinBid()) + require.Equal(t, uint64(200), got[0].GetBuilderBoostFactor()) + require.DeepEqual(t, pubkey, got[0].Pubkey) + require.Equal(t, primitives.Gwei(1000), got[0].Request.Preferences.GetMaxExecutionPayment()) +} + +// Duplicate urls in an SSZ body invalidate the whole request, matching JSON. +func TestParseBuilderPreferencesBody_SSZ_DuplicateURL(t *testing.T) { + entry := func() *eth.BuilderPreferenceV1 { + return ð.BuilderPreferenceV1{ + Url: "https://dup.example", + Request: ð.BuilderPreferencesRequestV1{ + Preferences: ð.BuilderPreferencesV1{}, + Auth: ð.SignedRequestAuthV1{Message: ð.RequestAuthV1{Data: []byte{1}}, Signature: make([]byte, 96)}, + }, + } + } + body, err := eth.BuilderPreferencesToSSZ([]*eth.BuilderPreferenceV1{entry(), entry()}).MarshalSSZ() + require.NoError(t, err) + r := httptest.NewRequest("POST", "/", bytes.NewBuffer(body)) + r.Header.Set("Content-Type", api.OctetStreamMediaType) + _, err = parseBuilderPreferencesBody(r) + require.ErrorContains(t, "share the same url", err) +} + func TestSubmitBuilderPreferencesHandler(t *testing.T) { pubkey := "0x" + repeatHex(48) entry := func(url string) string { diff --git a/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go b/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go index fc97bf545a57..f6bad1998e56 100644 --- a/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go +++ b/beacon-chain/rpc/eth/validator/handlers_block_gloas_test.go @@ -115,6 +115,35 @@ func TestProduceBlockV4_IncludePayloadTrue(t *testing.T) { require.Equal(t, "true", writer.Header().Get(api.ExecutionPayloadIncludedHeader)) } +// A builder-API win returns the winning url on GenericBeaconBlock.BuilderUrl; the +// handler must surface it as the Eth-Builder-Url response header for the publisher. +func TestProduceBlockV4_SetsBuilderUrlHeader(t *testing.T) { + params.SetupTestConfigCleanup(t) + cfg := params.BeaconConfig().Copy() + cfg.GloasForkEpoch = 0 + params.OverrideBeaconConfig(cfg) + + ctrl := gomock.NewController(t) + resp := gloasGenericBlockWithBuilder(1) + resp.BuilderUrl = "https://builder.example" + v1alpha1Server := mock2.NewMockBeaconNodeValidatorServer(ctrl) + v1alpha1Server.EXPECT().GetBeaconBlock(gomock.Any(), gomock.Any()).Return(resp, nil) + + server := &Server{ + V1Alpha1Server: v1alpha1Server, + SyncChecker: &mockSync.Sync{IsSyncing: false}, + OptimisticModeFetcher: &blockchainTesting.ChainService{}, + BlockRewardFetcher: &rewardtesting.MockBlockRewardFetcher{Rewards: &structs.BlockRewards{Total: "10"}}, + } + request := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://foo.example/eth/v4/validator/blocks/1?randao_reveal=%s&graffiti=%s", testRandao, testGraffiti), nil) + request.SetPathValue("slot", "1") + writer := httptest.NewRecorder() + writer.Body = &bytes.Buffer{} + server.ProduceBlockV4(writer, request) + assert.Equal(t, http.StatusOK, writer.Code) + require.Equal(t, "https://builder.example", writer.Header().Get(api.EthBuilderUrlHeader)) +} + // TestProduceBlockV4_IncludePayloadTrue_WithBlobs covers the blob path: the producer bundles // raw blobs and flat KZG proofs in GloasContents, which the v4 response embeds in the body. func TestProduceBlockV4_IncludePayloadTrue_WithBlobs(t *testing.T) { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go index 52b9a5813494..d05d62ef4511 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences.go @@ -20,8 +20,8 @@ func (vs *Server) SubmitBuilderPreferences(ctx context.Context, req *ethpb.Submi if req == nil || req.Request == nil { return nil, status.Error(codes.InvalidArgument, "builder preferences request is empty") } - if req.Url == "" { - return nil, status.Error(codes.InvalidArgument, "builder preferences request is missing the builder url") + if !validBuilderURL(req.Url) { + return nil, status.Error(codes.InvalidArgument, "builder preferences request has a malformed builder url") } // Not gated on Configured(): gloas builders are dialed per URL rather than the endpoint flag. if vs.BlockBuilder == nil { diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go index 83669d7ddfb3..8f578b54e987 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/builder_preferences_test.go @@ -34,7 +34,12 @@ func TestSubmitBuilderPreferences(t *testing.T) { t.Run("rejects missing url", func(t *testing.T) { vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} _, err := vs.SubmitBuilderPreferences(t.Context(), aotPrefReq("")) - require.ErrorContains(t, "missing the builder url", err) + require.ErrorContains(t, "malformed builder url", err) + }) + t.Run("rejects malformed url", func(t *testing.T) { + vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} + _, err := vs.SubmitBuilderPreferences(t.Context(), aotPrefReq("ftp://nohost")) + require.ErrorContains(t, "malformed builder url", err) }) t.Run("rejects empty request", func(t *testing.T) { vs := &Server{BlockBuilder: &builderTest.MockBuilderService{}} diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index a5ffcb733f13..5b79f14a9499 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -20,6 +20,10 @@ - Normalize legacy proposer settings on load so unset builder `enabled` stays disabled and both validator DB backends decode `max_execution_payment` identically. - Report the gas limit for v1 proposer settings via `GET /eth/v1/validator/config` instead of omitting it. - Decode empty builder `auth_data`/`pubkey` identically to unset across both validator DB backends. +- Mask credentials in builder-client error messages and trace spans so a builder URL with userinfo or a query token cannot leak. +- Send the inline builder-preferences produce-block body as SSZ; the beacon node accepts SSZ or JSON. +- Validate the builder url on the ahead-of-time preferences endpoint before dialing. +- Preserve the winning builder url when unwrapping a stateless self-build produce response. ### Removed diff --git a/proto/prysm/v1alpha1/BUILD.bazel b/proto/prysm/v1alpha1/BUILD.bazel index 3b6f9fcaba59..f6ddbc2bc51a 100644 --- a/proto/prysm/v1alpha1/BUILD.bazel +++ b/proto/prysm/v1alpha1/BUILD.bazel @@ -226,6 +226,8 @@ ssz_gloas_builder_api_objs = [ "SignedRequestAuthV1", "BuilderPreferencesV1", "BuilderPreferencesRequestV1", + "ProduceBuilderEntryV1", + "ProduceBuilderEntryListV1", ] ssz_gen_marshal( diff --git a/proto/prysm/v1alpha1/gloas_builder_api.go b/proto/prysm/v1alpha1/gloas_builder_api.go new file mode 100644 index 000000000000..5d377ee36c01 --- /dev/null +++ b/proto/prysm/v1alpha1/gloas_builder_api.go @@ -0,0 +1,69 @@ +package eth + +// neutralBuilderBoostFactorSSZ is the wire sentinel for an unset boost factor. +const neutralBuilderBoostFactorSSZ = 100 + +const blsPubkeyLengthSSZ = 48 + +// BuilderPreferencesToSSZ converts inline builder preferences to the SSZ +// produce-body form. Absent optionals collapse to sentinels: min_bid 0 (no floor), +// boost 100 (neutral), all-zero pubkey (no binding). +func BuilderPreferencesToSSZ(prefs []*BuilderPreferenceV1) *ProduceBuilderEntryListV1 { + entries := make([]*ProduceBuilderEntryV1, 0, len(prefs)) + for _, p := range prefs { + if p == nil { + continue + } + e := &ProduceBuilderEntryV1{ + Url: []byte(p.Url), + Request: p.Request, + MinBid: p.GetMinBid(), + BuilderBoostFactor: neutralBuilderBoostFactorSSZ, + Pubkey: make([]byte, blsPubkeyLengthSSZ), + } + if p.BuilderBoostFactor != nil { + e.BuilderBoostFactor = p.GetBuilderBoostFactor() + } + if len(p.Pubkey) == blsPubkeyLengthSSZ { + copy(e.Pubkey, p.Pubkey) + } + entries = append(entries, e) + } + return &ProduceBuilderEntryListV1{Entries: entries} +} + +// BuilderPreferencesFromSSZ converts the SSZ produce-body form back to inline +// preferences; an all-zero pubkey maps to no binding. +func BuilderPreferencesFromSSZ(list *ProduceBuilderEntryListV1) []*BuilderPreferenceV1 { + if list == nil { + return nil + } + out := make([]*BuilderPreferenceV1, 0, len(list.Entries)) + for _, e := range list.Entries { + if e == nil { + continue + } + minBid := e.MinBid + boost := e.BuilderBoostFactor + p := &BuilderPreferenceV1{ + Url: string(e.Url), + Request: e.Request, + MinBid: &minBid, + BuilderBoostFactor: &boost, + } + if !allZeroSSZ(e.Pubkey) { + p.Pubkey = e.Pubkey + } + out = append(out, p) + } + return out +} + +func allZeroSSZ(b []byte) bool { + for _, v := range b { + if v != 0 { + return false + } + } + return true +} diff --git a/proto/prysm/v1alpha1/gloas_builder_api.pb.go b/proto/prysm/v1alpha1/gloas_builder_api.pb.go index 90ec177ac7b0..6c711bda6f6c 100755 --- a/proto/prysm/v1alpha1/gloas_builder_api.pb.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.pb.go @@ -359,6 +359,126 @@ func (x *BuilderPreferenceV1) GetPubkey() []byte { return nil } +type ProduceBuilderEntryV1 struct { + state protoimpl.MessageState `protogen:"open.v1"` + Url []byte `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty" ssz-max:"2048"` + Request *BuilderPreferencesRequestV1 `protobuf:"bytes,2,opt,name=request,proto3" json:"request,omitempty"` + MinBid github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei `protobuf:"varint,3,opt,name=min_bid,json=minBid,proto3" json:"min_bid,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei"` + BuilderBoostFactor uint64 `protobuf:"varint,4,opt,name=builder_boost_factor,json=builderBoostFactor,proto3" json:"builder_boost_factor,omitempty"` + Pubkey []byte `protobuf:"bytes,5,opt,name=pubkey,proto3" json:"pubkey,omitempty" ssz-size:"48"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProduceBuilderEntryV1) Reset() { + *x = ProduceBuilderEntryV1{} + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProduceBuilderEntryV1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProduceBuilderEntryV1) ProtoMessage() {} + +func (x *ProduceBuilderEntryV1) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProduceBuilderEntryV1.ProtoReflect.Descriptor instead. +func (*ProduceBuilderEntryV1) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP(), []int{6} +} + +func (x *ProduceBuilderEntryV1) GetUrl() []byte { + if x != nil { + return x.Url + } + return nil +} + +func (x *ProduceBuilderEntryV1) GetRequest() *BuilderPreferencesRequestV1 { + if x != nil { + return x.Request + } + return nil +} + +func (x *ProduceBuilderEntryV1) GetMinBid() github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei { + if x != nil { + return x.MinBid + } + return github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei(0) +} + +func (x *ProduceBuilderEntryV1) GetBuilderBoostFactor() uint64 { + if x != nil { + return x.BuilderBoostFactor + } + return 0 +} + +func (x *ProduceBuilderEntryV1) GetPubkey() []byte { + if x != nil { + return x.Pubkey + } + return nil +} + +type ProduceBuilderEntryListV1 struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entries []*ProduceBuilderEntryV1 `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty" ssz-max:"256"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProduceBuilderEntryListV1) Reset() { + *x = ProduceBuilderEntryListV1{} + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProduceBuilderEntryListV1) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProduceBuilderEntryListV1) ProtoMessage() {} + +func (x *ProduceBuilderEntryListV1) ProtoReflect() protoreflect.Message { + mi := &file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProduceBuilderEntryListV1.ProtoReflect.Descriptor instead. +func (*ProduceBuilderEntryListV1) Descriptor() ([]byte, []int) { + return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP(), []int{7} +} + +func (x *ProduceBuilderEntryListV1) GetEntries() []*ProduceBuilderEntryV1 { + if x != nil { + return x.Entries + } + return nil +} + var File_proto_prysm_v1alpha1_gloas_builder_api_proto protoreflect.FileDescriptor var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ @@ -436,11 +556,38 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc = []byte{ 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, - 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, - 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x73, 0x74, 0x5f, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x22, 0xb2, 0x02, 0x0a, 0x15, 0x50, + 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x56, 0x31, 0x12, 0x1a, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x42, 0x08, 0x92, 0xb5, 0x18, 0x04, 0x32, 0x30, 0x34, 0x38, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x12, 0x4c, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x32, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, + 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x56, 0x31, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5d, + 0x0a, 0x07, 0x6d, 0x69, 0x6e, 0x5f, 0x62, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, + 0x73, 0x6d, 0x2f, 0x76, 0x37, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x2d, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69, 0x74, 0x69, 0x76, 0x65, 0x73, + 0x2e, 0x47, 0x77, 0x65, 0x69, 0x52, 0x06, 0x6d, 0x69, 0x6e, 0x42, 0x69, 0x64, 0x12, 0x30, 0x0a, + 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6f, 0x6f, 0x73, 0x74, 0x5f, 0x66, + 0x61, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x42, 0x6f, 0x6f, 0x73, 0x74, 0x46, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x12, + 0x1e, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, + 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x34, 0x38, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6b, 0x65, 0x79, 0x22, + 0x6c, 0x0a, 0x19, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x65, + 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x56, 0x31, 0x12, 0x4f, 0x0a, 0x07, + 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, + 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x74, 0x68, 0x2e, 0x76, 0x31, 0x61, + 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x65, 0x42, 0x75, 0x69, + 0x6c, 0x64, 0x65, 0x72, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x56, 0x31, 0x42, 0x07, 0x92, 0xb5, 0x18, + 0x03, 0x32, 0x35, 0x36, 0x52, 0x07, 0x65, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x42, 0x3b, 0x5a, + 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, + 0x37, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x31, + 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x65, 0x74, 0x68, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -455,7 +602,7 @@ func file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescGZIP() []byte { return file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDescData } -var file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_proto_prysm_v1alpha1_gloas_builder_api_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_proto_prysm_v1alpha1_gloas_builder_api_proto_goTypes = []any{ (*RequestAuthV1)(nil), // 0: ethereum.eth.v1alpha1.RequestAuthV1 (*SignedRequestAuthV1)(nil), // 1: ethereum.eth.v1alpha1.SignedRequestAuthV1 @@ -463,6 +610,8 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_goTypes = []any{ (*BuilderPreferencesRequestV1)(nil), // 3: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 (*SubmitBuilderPreferencesRequest)(nil), // 4: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest (*BuilderPreferenceV1)(nil), // 5: ethereum.eth.v1alpha1.BuilderPreferenceV1 + (*ProduceBuilderEntryV1)(nil), // 6: ethereum.eth.v1alpha1.ProduceBuilderEntryV1 + (*ProduceBuilderEntryListV1)(nil), // 7: ethereum.eth.v1alpha1.ProduceBuilderEntryListV1 } var file_proto_prysm_v1alpha1_gloas_builder_api_proto_depIdxs = []int32{ 0, // 0: ethereum.eth.v1alpha1.SignedRequestAuthV1.message:type_name -> ethereum.eth.v1alpha1.RequestAuthV1 @@ -470,11 +619,13 @@ var file_proto_prysm_v1alpha1_gloas_builder_api_proto_depIdxs = []int32{ 1, // 2: ethereum.eth.v1alpha1.BuilderPreferencesRequestV1.auth:type_name -> ethereum.eth.v1alpha1.SignedRequestAuthV1 3, // 3: ethereum.eth.v1alpha1.SubmitBuilderPreferencesRequest.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 3, // 4: ethereum.eth.v1alpha1.BuilderPreferenceV1.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name + 3, // 5: ethereum.eth.v1alpha1.ProduceBuilderEntryV1.request:type_name -> ethereum.eth.v1alpha1.BuilderPreferencesRequestV1 + 6, // 6: ethereum.eth.v1alpha1.ProduceBuilderEntryListV1.entries:type_name -> ethereum.eth.v1alpha1.ProduceBuilderEntryV1 + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_proto_prysm_v1alpha1_gloas_builder_api_proto_init() } @@ -489,7 +640,7 @@ func file_proto_prysm_v1alpha1_gloas_builder_api_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_prysm_v1alpha1_gloas_builder_api_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index 2320034e57e9..33780d323d79 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -64,3 +64,21 @@ message BuilderPreferenceV1 { // When set, only bids signed by this builder key are accepted from this url. bytes pubkey = 5; } + +// ProduceBuilderEntryV1 is the SSZ-encodable form of one produceBlockV4 builder +// entry; absent optionals collapse to sentinels (min_bid 0, boost 100, zero pubkey). +message ProduceBuilderEntryV1 { + bytes url = 1 [ (ethereum.eth.ext.ssz_max) = "2048" ]; + BuilderPreferencesRequestV1 request = 2; + uint64 min_bid = 3 [ + (ethereum.eth.ext.cast_type) = + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Gwei" + ]; + uint64 builder_boost_factor = 4; + bytes pubkey = 5 [ (ethereum.eth.ext.ssz_size) = "48" ]; +} + +message ProduceBuilderEntryListV1 { + repeated ProduceBuilderEntryV1 entries = 1 + [ (ethereum.eth.ext.ssz_max) = "256" ]; +} diff --git a/proto/prysm/v1alpha1/gloas_builder_api.ssz.go b/proto/prysm/v1alpha1/gloas_builder_api.ssz.go index 78618ca2612a..21340d54f747 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.ssz.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.ssz.go @@ -377,3 +377,297 @@ func (b *BuilderPreferencesRequestV1) HashTreeRootWith(hh *ssz.Hasher) (err erro hh.Merkleize(indx) return } + +// MarshalSSZ ssz marshals the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the ProduceBuilderEntryV1 object to a target array +func (p *ProduceBuilderEntryV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(72) + + // Offset (0) 'Url' + dst = ssz.WriteOffset(dst, offset) + offset += len(p.Url) + + // Offset (1) 'Request' + dst = ssz.WriteOffset(dst, offset) + if p.Request == nil { + p.Request = new(BuilderPreferencesRequestV1) + } + offset += p.Request.SizeSSZ() + + // Field (2) 'MinBid' + dst = ssz.MarshalUint(dst, p.MinBid) + + // Field (3) 'BuilderBoostFactor' + dst = ssz.MarshalUint(dst, p.BuilderBoostFactor) + + // Field (4) 'Pubkey' + if size := len(p.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("--.Pubkey", size, 48) + return + } + dst = append(dst, p.Pubkey...) + + // Field (0) 'Url' + if size := len(p.Url); size > 2048 { + err = ssz.ErrBytesLengthFn("--.Url", size, 2048) + return + } + dst = append(dst, p.Url...) + + // Field (1) 'Request' + if dst, err = p.Request.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 72 { + return ssz.ErrSize + } + + tail := buf + var o0, o1 uint64 + + // Offset (0) 'Url' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 72 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (1) 'Request' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } + + // Field (2) 'MinBid' + p.MinBid = ssz.UnmarshallUint[github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei](buf[8:16]) + + // Field (3) 'BuilderBoostFactor' + p.BuilderBoostFactor = ssz.UnmarshallUint[uint64](buf[16:24]) + + // Field (4) 'Pubkey' + if cap(p.Pubkey) == 0 { + p.Pubkey = make([]byte, 0, len(buf[24:72])) + } + p.Pubkey = append(p.Pubkey, buf[24:72]...) + + // Field (0) 'Url' + { + buf = tail[o0:o1] + if len(buf) > 2048 { + return ssz.ErrBytesLength + } + if cap(p.Url) == 0 { + p.Url = make([]byte, 0, len(buf)) + } + p.Url = append(p.Url, buf...) + } + + // Field (1) 'Request' + { + buf = tail[o1:] + if p.Request == nil { + p.Request = new(BuilderPreferencesRequestV1) + } + if err = p.Request.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) SizeSSZ() (size int) { + size = 72 + + // Field (0) 'Url' + size += len(p.Url) + + // Field (1) 'Request' + if p.Request == nil { + p.Request = new(BuilderPreferencesRequestV1) + } + size += p.Request.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the ProduceBuilderEntryV1 object with a hasher +func (p *ProduceBuilderEntryV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Url' + { + elemIndx := hh.Index() + byteLen := uint64(len(p.Url)) + if byteLen > 2048 { + err = ssz.ErrIncorrectListSize + return + } + hh.AppendBytes32(p.Url) + hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32) + } + + // Field (1) 'Request' + if err = p.Request.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'MinBid' + ssz.PutUint(hh, p.MinBid) + + // Field (3) 'BuilderBoostFactor' + ssz.PutUint(hh, p.BuilderBoostFactor) + + // Field (4) 'Pubkey' + if size := len(p.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("--.Pubkey", size, 48) + return + } + hh.PutBytes(p.Pubkey) + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the ProduceBuilderEntryListV1 object to a target array +func (p *ProduceBuilderEntryListV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(4) + + // Offset (0) 'Entries' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(p.Entries); ii++ { + offset += 4 + offset += p.Entries[ii].SizeSSZ() + } + + // Field (0) 'Entries' + if size := len(p.Entries); size > 256 { + err = ssz.ErrListTooBigFn("--.Entries", size, 256) + return + } + { + offset = 4 * len(p.Entries) + for ii := 0; ii < len(p.Entries); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += p.Entries[ii].SizeSSZ() + } + } + for ii := 0; ii < len(p.Entries); ii++ { + if dst, err = p.Entries[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 4 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Entries' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 4 { + return ssz.ErrInvalidVariableOffset + } + + // Field (0) 'Entries' + { + buf = tail[o0:] + num, err := ssz.DecodeDynamicLength(buf, 256) + if err != nil { + return err + } + p.Entries = make([]*ProduceBuilderEntryV1, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if p.Entries[indx] == nil { + p.Entries[indx] = new(ProduceBuilderEntryV1) + } + if err = p.Entries[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) SizeSSZ() (size int) { + size = 4 + + // Field (0) 'Entries' + for ii := 0; ii < len(p.Entries); ii++ { + size += 4 + size += p.Entries[ii].SizeSSZ() + } + + return +} + +// HashTreeRoot ssz hashes the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the ProduceBuilderEntryListV1 object with a hasher +func (p *ProduceBuilderEntryListV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Entries' + { + subIndx := hh.Index() + num := uint64(len(p.Entries)) + if num > 256 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range p.Entries { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 256) + } + + hh.Merkleize(indx) + return +} diff --git a/proto/prysm/v1alpha1/gloas_builder_api_test.go b/proto/prysm/v1alpha1/gloas_builder_api_test.go new file mode 100644 index 000000000000..de8c4bcfaf09 --- /dev/null +++ b/proto/prysm/v1alpha1/gloas_builder_api_test.go @@ -0,0 +1,53 @@ +package eth + +import ( + "testing" + + "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + "github.com/OffchainLabs/prysm/v7/testing/require" +) + +func TestBuilderPreferencesSSZRoundTrip(t *testing.T) { + pubkey := make([]byte, 48) + pubkey[0] = 0xAB + boost := uint64(200) + minBid := primitives.Gwei(500) + prefs := []*BuilderPreferenceV1{ + { + Url: "https://a.example", + Request: &BuilderPreferencesRequestV1{ + Preferences: &BuilderPreferencesV1{MaxExecutionPayment: 1000}, + Auth: &SignedRequestAuthV1{Message: &RequestAuthV1{Data: []byte("auth"), Slot: 7}, Signature: make([]byte, 96)}, + }, + MinBid: &minBid, + BuilderBoostFactor: &boost, + Pubkey: pubkey, + }, + { + // Optionals absent: min_bid, boost, and pubkey resolve to sentinels. + Url: "https://b.example", + Request: &BuilderPreferencesRequestV1{ + Preferences: &BuilderPreferencesV1{}, + Auth: &SignedRequestAuthV1{Message: &RequestAuthV1{}, Signature: make([]byte, 96)}, + }, + }, + } + + ssz, err := BuilderPreferencesToSSZ(prefs).MarshalSSZ() + require.NoError(t, err) + list := &ProduceBuilderEntryListV1{} + require.NoError(t, list.UnmarshalSSZ(ssz)) + got := BuilderPreferencesFromSSZ(list) + + require.Equal(t, 2, len(got)) + require.Equal(t, "https://a.example", got[0].Url) + require.Equal(t, primitives.Gwei(500), got[0].GetMinBid()) + require.Equal(t, uint64(200), got[0].GetBuilderBoostFactor()) + require.DeepEqual(t, pubkey, got[0].Pubkey) + require.Equal(t, primitives.Gwei(1000), got[0].Request.Preferences.GetMaxExecutionPayment()) + require.DeepEqual(t, []byte("auth"), got[0].Request.Auth.Message.Data) + + require.Equal(t, primitives.Gwei(0), got[1].GetMinBid()) + require.Equal(t, uint64(100), got[1].GetBuilderBoostFactor()) + require.IsNil(t, got[1].Pubkey) +} diff --git a/validator/client/beacon-api/builder_preferences.go b/validator/client/beacon-api/builder_preferences.go index 3aa806535732..5a0b745a4ae0 100644 --- a/validator/client/beacon-api/builder_preferences.go +++ b/validator/client/beacon-api/builder_preferences.go @@ -11,17 +11,6 @@ import ( "github.com/pkg/errors" ) -// builderEntryReq is one element of the produceBlockV4 POST body (beacon-APIs -// #630): a bare array of per-builder entries. -type builderEntryReq struct { - Url string `json:"url"` - Auth *signedAuthReq `json:"auth"` - Pubkey string `json:"pubkey,omitempty"` - MaxExecutionPayment string `json:"max_execution_payment,omitempty"` - MinBid string `json:"min_bid,omitempty"` - BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` -} - // builderPreferenceEntryReq is one element of the ahead-of-time preferences POST // body for /eth/v1/validator/builder_preferences/{pubkey}. type builderPreferenceEntryReq struct { @@ -50,31 +39,17 @@ func signedAuthFromConsensus(auth *ethpb.SignedRequestAuthV1) *signedAuthReq { } } -// marshalBuilderPreferences encodes inline builder preferences into the #630 -// produce-block body: a bare array of builder entries. +// marshalBuilderPreferences SSZ-encodes inline builder preferences for the #630 +// produce-block body (octet-stream request variant). func marshalBuilderPreferences(prefs []*ethpb.BuilderPreferenceV1) ([]byte, error) { - entries := make([]*builderEntryReq, 0, len(prefs)) + valid := make([]*ethpb.BuilderPreferenceV1, 0, len(prefs)) for _, p := range prefs { if p == nil || p.Request == nil || p.Request.Auth == nil || p.Request.Auth.Message == nil { continue } - entry := &builderEntryReq{ - Url: p.Url, - Auth: signedAuthFromConsensus(p.Request.Auth), - MaxExecutionPayment: strconv.FormatUint(uint64(p.Request.Preferences.GetMaxExecutionPayment()), 10), - } - if len(p.Pubkey) > 0 { - entry.Pubkey = hexutil.Encode(p.Pubkey) - } - if p.MinBid != nil { - entry.MinBid = strconv.FormatUint(uint64(p.GetMinBid()), 10) - } - if p.BuilderBoostFactor != nil { - entry.BuilderBoostFactor = strconv.FormatUint(p.GetBuilderBoostFactor(), 10) - } - entries = append(entries, entry) + valid = append(valid, p) } - return json.Marshal(entries) + return ethpb.BuilderPreferencesToSSZ(valid).MarshalSSZ() } // submitBuilderPreferences pushes one signed builder preference ahead of the diff --git a/validator/client/beacon-api/get_beacon_block.go b/validator/client/beacon-api/get_beacon_block.go index 794647ee80e4..9cf5789071b7 100644 --- a/validator/client/beacon-api/get_beacon_block.go +++ b/validator/client/beacon-api/get_beacon_block.go @@ -75,13 +75,12 @@ func (c *beaconApiValidatorClient) beaconBlockV4(ctx context.Context, slot primi var header http.Header var err error if len(builderPreferences) > 0 { - // JIT (beacon-APIs #625): POST the per-builder preferences inline. + // JIT (beacon-APIs #625): POST the per-builder preferences inline as SSZ. body, mErr := marshalBuilderPreferences(builderPreferences) if mErr != nil { return nil, errors.Wrap(mErr, "could not marshal builder preferences") } - headers := map[string]string{"Content-Type": api.JsonMediaType} - data, header, err = c.handler.PostSSZ(ctx, queryUrl, headers, bytes.NewBuffer(body)) + data, header, err = c.handler.PostSSZ(ctx, queryUrl, nil, bytes.NewBuffer(body)) } else { data, header, err = c.handler.GetSSZ(ctx, queryUrl) } diff --git a/validator/client/grpc-api/grpc_validator_client.go b/validator/client/grpc-api/grpc_validator_client.go index 928e61e545a0..57241ac00fc5 100644 --- a/validator/client/grpc-api/grpc_validator_client.go +++ b/validator/client/grpc-api/grpc_validator_client.go @@ -225,7 +225,13 @@ func (c *grpcValidatorClient) BeaconBlock(ctx context.Context, in *ethpb.BlockRe if c.stateless && gc.GetExecutionPayloadEnvelope() != nil { c.envelopeCache.Add(in.Slot, gc.ExecutionPayloadEnvelope, gc.Blobs, gc.KzgProofs) } - return ðpb.GenericBeaconBlock{Block: ðpb.GenericBeaconBlock_Gloas{Gloas: gc.Block}}, nil + // Preserve the top-level routing fields; only the payload contents are unbundled. + return ðpb.GenericBeaconBlock{ + Block: ðpb.GenericBeaconBlock_Gloas{Gloas: gc.Block}, + BuilderUrl: resp.GetBuilderUrl(), + IsBlinded: resp.GetIsBlinded(), + PayloadValue: resp.GetPayloadValue(), + }, nil } func (c *grpcValidatorClient) FeeRecipientByPubKey(ctx context.Context, in *ethpb.FeeRecipientByPubKeyRequest) (*ethpb.FeeRecipientByPubKeyResponse, error) { diff --git a/validator/client/grpc-api/grpc_validator_client_test.go b/validator/client/grpc-api/grpc_validator_client_test.go index 6f9fa9a4a6b1..ecc645698505 100644 --- a/validator/client/grpc-api/grpc_validator_client_test.go +++ b/validator/client/grpc-api/grpc_validator_client_test.go @@ -416,6 +416,22 @@ func TestBeaconBlock(t *testing.T) { require.IsNil(t, proofs) }, }, + { + name: "top-level routing fields survive the contents unwrap", + stateless: true, + resp: ð.GenericBeaconBlock{ + Block: gloasContentsResp.Block, + BuilderUrl: "https://builder.example", + IsBlinded: true, + PayloadValue: "1234", + }, + verify: func(t *testing.T, _ *grpcValidatorClient, got *eth.GenericBeaconBlock, err error) { + require.NoError(t, err) + require.Equal(t, "https://builder.example", got.GetBuilderUrl()) + require.Equal(t, true, got.GetIsBlinded()) + require.Equal(t, "1234", got.GetPayloadValue()) + }, + }, } for _, tt := range tests { diff --git a/validator/client/propose_test.go b/validator/client/propose_test.go index 7bbbedb417f3..bf90770cc96d 100644 --- a/validator/client/propose_test.go +++ b/validator/client/propose_test.go @@ -281,6 +281,36 @@ func TestProposeBlock_ProposeBlockFailed(t *testing.T) { } } +// The winning builder url from the produce response must ride the published block +// so the beacon node can forward it to the builder. +func TestProposeBlock_CopiesBuilderUrl(t *testing.T) { + validator, m, validatorKey, finish := setup(t, true) + defer finish() + var pubKey [fieldparams.BLSPubkeyLength]byte + copy(pubKey[:], validatorKey.PublicKey().Marshal()) + + m.validatorClient.EXPECT().DomainData(gomock.Any(), gomock.Any()). + Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil) + m.validatorClient.EXPECT().BeaconBlock(gomock.Any(), gomock.AssignableToTypeOf(ðpb.BlockRequest{})). + Return(ðpb.GenericBeaconBlock{ + Block: ðpb.GenericBeaconBlock_Phase0{Phase0: util.NewBeaconBlock().Block}, + BuilderUrl: "https://builder.example", + }, nil) + m.validatorClient.EXPECT().DomainData(gomock.Any(), gomock.Any()). + Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil) + + var captured *ethpb.GenericSignedBeaconBlock + m.validatorClient.EXPECT().ProposeBeaconBlock(gomock.Any(), gomock.AssignableToTypeOf(ðpb.GenericSignedBeaconBlock{})). + DoAndReturn(func(_ context.Context, block *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) { + captured = block + return ðpb.ProposeResponse{BlockRoot: make([]byte, 32)}, nil + }) + + validator.ProposeBlock(t.Context(), 1, pubKey) + require.NotNil(t, captured) + require.Equal(t, "https://builder.example", captured.BuilderUrl) +} + func TestProposeBlock_BlocksDoubleProposal(t *testing.T) { slot := params.BeaconConfig().SlotsPerEpoch.Mul(5).Add(2) var blockGraffiti [32]byte From ef51585c6a5a8abb087f246fa2c1fd43fd0dee5b Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 27 Jul 2026 10:11:09 -0500 Subject: [PATCH 17/19] keymanager api from 87 to https://github.com/ethereum/keymanager-APIs/pull/88 --- .../james-prysm_validator-config-endpoint.md | 6 +- config/proposer/settings.go | 3 +- validator/rpc/handlers_validator_config.go | 344 +++++++--------- .../rpc/handlers_validator_config_test.go | 377 ++++++------------ validator/rpc/server.go | 5 +- validator/rpc/structs.go | 53 +-- 6 files changed, 306 insertions(+), 482 deletions(-) diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index 5b79f14a9499..6f40ef5d02cb 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -1,6 +1,6 @@ ### Added -- Add `GET`/`POST /eth/v1/validator/config` keymanager endpoints for managing per-key proposer and builder preferences as a single document (keymanager-APIs #87). +- Add `GET`/`POST`/`DELETE /eth/v1/validator/{pubkey}/builders` keymanager endpoints for managing a key's builder configuration (keymanager-APIs #88). The fee recipient, gas limit and graffiti endpoints are unchanged. - Add per-key `min_bid` and `builder_boost_factor` to Gloas bid selection, and per-key `builder_boost_factor` to pre-Gloas block requests. - Accept `POST /eth/v4/validator/blocks/{slot}` with inline builder preferences (beacon-APIs #625). @@ -10,7 +10,8 @@ - For v2 settings, per-key builder settings no longer require a per-key fee recipient to register; the default fee recipient is used. - Send builder preferences inline with the block request instead of caching them on the beacon node; request auths sign opaque `auth_data` per builder-specs #165, with the builder URL carried separately. - Merge file- or URL-loaded proposer settings per key: the file only overrides keys it names, and the settings schema version never regresses. -- Reject duplicate builder urls when setting the validator config; previously persisted duplicates keep the first entry for each url. +- Reject builder entries that duplicate a full `(url, auth_data, builder_pubkey)` tuple or repeat a `builder_pubkey`; a builder entry needs at least one of `url` or `builder_pubkey`. +- A per-key builders list replaces the validator client's builders; an empty list uses none (p2p bids only) while an omitted list inherits. - For v2 settings, `--enable-builder` sets the default builder toggle only when it is unset; an explicit `enabled` and per-key entries are left to field-level inheritance. - Enforce the per-builder `pubkey` from the validator config during bid selection: when set, only bids from that builder key are accepted for the entry's url. @@ -18,7 +19,6 @@ ### Fixed - Normalize legacy proposer settings on load so unset builder `enabled` stays disabled and both validator DB backends decode `max_execution_payment` identically. -- Report the gas limit for v1 proposer settings via `GET /eth/v1/validator/config` instead of omitting it. - Decode empty builder `auth_data`/`pubkey` identically to unset across both validator DB backends. - Mask credentials in builder-client error messages and trace spans so a builder URL with userinfo or a query token cannot leak. - Send the inline builder-preferences produce-block body as SSZ; the beacon node accepts SSZ or JSON. diff --git a/config/proposer/settings.go b/config/proposer/settings.go index 14c77f6f5e24..ccf6fc6dc682 100644 --- a/config/proposer/settings.go +++ b/config/proposer/settings.go @@ -184,7 +184,8 @@ func EffectiveBuilderConfig(perKey, def *BuilderConfig) *BuilderConfig { if eff.GasLimit == 0 { eff.GasLimit = def.GasLimit } - if len(eff.Builders) == 0 { + // A nil list inherits the default's; a non-nil empty list means "use no builders". + if eff.Builders == nil { eff.Builders = def.Builders } return eff diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index 2d691f681d52..ab09e1511ac4 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -2,84 +2,89 @@ package rpc import ( "encoding/json" + "fmt" "net/http" "net/url" "strconv" + "github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/eth/shared" fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams" "github.com/OffchainLabs/prysm/v7/config/proposer" "github.com/OffchainLabs/prysm/v7/consensus-types/validator" "github.com/OffchainLabs/prysm/v7/encoding/bytesutil" - "github.com/OffchainLabs/prysm/v7/io/logs" "github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace" "github.com/OffchainLabs/prysm/v7/network/httputil" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/pkg/errors" ) -// Per-key statuses; not_found is unused because Prysm supports pre-provisioning. -const ( - configStatusSet = "set" - configStatusError = "error" -) +const maxBuilderEntries = 64 -// GetValidatorConfig implements GET /eth/v1/validator/config from keymanager-APIs #87. -// It returns the explicitly configured per-key fragments plus the read-only default_config. -func (s *Server) GetValidatorConfig(w http.ResponseWriter, r *http.Request) { - _, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.GetValidatorConfig") +// GetBuilders implements GET /eth/v1/validator/{pubkey}/builders (keymanager-APIs #88). +// It returns the key's builder configuration resolved against default_config so +// re-submitting the response reproduces the same behavior. +func (s *Server) GetBuilders(w http.ResponseWriter, r *http.Request) { + _, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.GetBuilders") defer span.End() if s.validatorService == nil { httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable) return } - - filter, ok := parsePubkeyFilter(w, r) + _, pubkey, ok := shared.HexFromRoute(w, r, "pubkey", fieldparams.BLSPubkeyLength) if !ok { return } - settings := s.validatorService.ProposerSettings() - resp := &GetValidatorConfigResponse{Data: &ValidatorConfigData{Configs: map[string]*ValidatorConfig{}}} - if settings == nil { - httputil.WriteJson(w, resp) - return - } - if settings.DefaultConfig != nil { - resp.Data.DefaultConfig = validatorConfigFromOption(settings.DefaultConfig) - } - for pk, opt := range settings.ProposeConfig { - if filter != nil { - if _, wanted := filter[pk]; !wanted { - continue - } + eff := resolveBuilders(s.validatorService.ProposerSettings(), bytesutil.ToBytes48(pubkey)) + out := builderConfigJSONFromConsensus(eff) + if out.Enabled == nil { + disabled := false + out.Enabled = &disabled + } + // The resolved response always states a concrete builders list. + if out.Builders == nil { + out.Builders = []*BuilderEntryJson{} + } + for _, e := range out.Builders { + if e.MinBid == nil { + e.MinBid = out.DefaultMinBid + } + if e.BuilderBoostFactor == nil { + e.BuilderBoostFactor = out.DefaultBuilderBoostFactor } - resp.Data.Configs[hexutil.Encode(pk[:])] = validatorConfigFromOption(opt) } - httputil.WriteJson(w, resp) + httputil.WriteJson(w, out) } -// SetValidatorConfig implements POST /eth/v1/validator/config from keymanager-APIs #87. -// Each submitted document REPLACES the key's configured fragment. An empty document -// clears the key. Keys are applied independently with a per-key status. -func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { - ctx, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.SetValidatorConfig") +// SetBuilders implements POST /eth/v1/validator/{pubkey}/builders. It replaces the +// key's builder configuration in full, leaving fee recipient, gas limit and graffiti +// untouched. +func (s *Server) SetBuilders(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.SetBuilders") defer span.End() if s.validatorService == nil { httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable) return } + _, pubkey, ok := shared.HexFromRoute(w, r, "pubkey", fieldparams.BLSPubkeyLength) + if !ok { + return + } - // Unrecognized fields are ignored for forward compatibility. - var req SetValidatorConfigRequest - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest) + var body BuilderConfigJson + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + httputil.HandleError(w, "Could not decode builder config: "+err.Error(), http.StatusBadRequest) return } - if req.Configs == nil { - httputil.HandleError(w, "No configs submitted", http.StatusBadRequest) + if body.Enabled == nil { + httputil.HandleError(w, "enabled is required", http.StatusBadRequest) + return + } + bc, err := builderConfigFromJSON(&body) + if err != nil { + httputil.HandleError(w, err.Error(), http.StatusBadRequest) return } @@ -96,124 +101,103 @@ func (s *Server) SetValidatorConfig(w http.ResponseWriter, r *http.Request) { if settings.ProposeConfig == nil { settings.ProposeConfig = make(map[[fieldparams.BLSPubkeyLength]byte]*proposer.Option) } - - resp := &SetValidatorConfigResponse{Data: make(map[string]*ValidatorConfigStatus, len(req.Configs))} - changed := false - for rawPubkey, cfg := range req.Configs { - pubkey, err := decodePubkey(rawPubkey) - if err != nil { - resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusError, Message: err.Error()} - continue - } - // Unknown keys are stored too; the config applies once the key is added. - if cfg == nil || cfg.isEmpty() { - delete(settings.ProposeConfig, pubkey) - resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusSet} - changed = true - continue - } - opt, err := optionFromValidatorConfig(cfg) - if err != nil { - resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusError, Message: err.Error()} - continue - } - settings.ProposeConfig[pubkey] = opt - resp.Data[rawPubkey] = &ValidatorConfigStatus{Status: configStatusSet} - changed = true + key := bytesutil.ToBytes48(pubkey) + opt := settings.ProposeConfig[key] + if opt == nil { + opt = &proposer.Option{} + settings.ProposeConfig[key] = opt } + opt.BuilderConfig = bc - if changed { - if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil { - httputil.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError) - return - } + if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil { + httputil.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError) + return } - httputil.WriteJson(w, resp) + w.WriteHeader(http.StatusAccepted) } -func parsePubkeyFilter(w http.ResponseWriter, r *http.Request) (map[[fieldparams.BLSPubkeyLength]byte]bool, bool) { - raw := r.URL.Query()["pubkeys"] - if len(raw) == 0 { - return nil, true - } - filter := make(map[[fieldparams.BLSPubkeyLength]byte]bool, len(raw)) - for _, p := range raw { - pubkey, err := decodePubkey(p) - if err != nil { - httputil.HandleError(w, "Invalid pubkey "+p+": "+err.Error(), http.StatusBadRequest) - return nil, false - } - filter[pubkey] = true - } - return filter, true -} +// DeleteBuilders implements DELETE /eth/v1/validator/{pubkey}/builders. It removes +// the key's builder configuration so the key follows the validator client defaults; +// this differs from enabled:false. +func (s *Server) DeleteBuilders(w http.ResponseWriter, r *http.Request) { + ctx, span := trace.StartSpan(r.Context(), "validator.keymanagerAPI.DeleteBuilders") + defer span.End() -func decodePubkey(raw string) ([fieldparams.BLSPubkeyLength]byte, error) { - var out [fieldparams.BLSPubkeyLength]byte - decoded, err := hexutil.Decode(raw) - if err != nil { - return out, errors.Wrap(err, "invalid public key") + if s.validatorService == nil { + httputil.HandleError(w, "Validator service not ready.", http.StatusServiceUnavailable) + return } - if len(decoded) != fieldparams.BLSPubkeyLength { - return out, errors.Errorf("public key %s is not %d bytes", raw, fieldparams.BLSPubkeyLength) + rawPubkey, pubkey, ok := shared.HexFromRoute(w, r, "pubkey", fieldparams.BLSPubkeyLength) + if !ok { + return } - return bytesutil.ToBytes48(decoded), nil -} -func (c *ValidatorConfig) isEmpty() bool { - return c.FeeRecipient == nil && c.TargetGasLimit == nil && c.Graffiti == nil && c.Builder == nil -} + s.proposerSettingsLock.Lock() + defer s.proposerSettingsLock.Unlock() -// validatorConfigFromOption serializes only the explicitly configured fields, so -// tooling can tell an explicit value from an inherited one. -func validatorConfigFromOption(opt *proposer.Option) *ValidatorConfig { - cfg := &ValidatorConfig{} - if opt == nil { - return cfg + settings := s.validatorService.ProposerSettings() + key := bytesutil.ToBytes48(pubkey) + if settings == nil || settings.ProposeConfig == nil { + httputil.HandleError(w, fmt.Sprintf("No builder configuration found for pubkey %q", rawPubkey), http.StatusNotFound) + return } - if opt.FeeRecipientConfig != nil { - addr := opt.FeeRecipientConfig.FeeRecipient.Hex() - cfg.FeeRecipient = &addr + opt, found := settings.ProposeConfig[key] + if !found || opt == nil || opt.BuilderConfig == nil { + httputil.HandleError(w, fmt.Sprintf("No builder configuration found for pubkey %q", rawPubkey), http.StatusNotFound) + return } - if opt.GasLimit != 0 { - cfg.TargetGasLimit = strPtr(formatUint(opt.GasLimit)) - } else if opt.BuilderConfig != nil && opt.BuilderConfig.GasLimit != 0 { - cfg.TargetGasLimit = strPtr(formatUint(opt.BuilderConfig.GasLimit)) + + settings = settings.Clone() + settings.ProposeConfig[key].BuilderConfig = nil + if err := s.validatorService.SetProposerSettings(ctx, settings); err != nil { + httputil.HandleError(w, "Could not set proposer settings: "+err.Error(), http.StatusInternalServerError) + return } - if opt.GraffitiConfig != nil { - g := opt.GraffitiConfig.Graffiti - cfg.Graffiti = &g + w.WriteHeader(http.StatusNoContent) +} + +func resolveBuilders(settings *proposer.Settings, key [fieldparams.BLSPubkeyLength]byte) *proposer.BuilderConfig { + var perKey, def *proposer.BuilderConfig + if settings != nil { + if settings.DefaultConfig != nil { + def = settings.DefaultConfig.BuilderConfig + } + if opt, ok := settings.ProposeConfig[key]; ok && opt != nil { + perKey = opt.BuilderConfig + } } - if opt.BuilderConfig != nil { - cfg.Builder = builderConfigJSONFromConsensus(opt.BuilderConfig) + if eff := proposer.EffectiveBuilderConfig(perKey, def); eff != nil { + return eff } - return cfg + return &proposer.BuilderConfig{} } func builderConfigJSONFromConsensus(bc *proposer.BuilderConfig) *BuilderConfigJson { out := &BuilderConfigJson{ - Enabled: bc.Enabled, - Proxy: bc.Proxy, - MaxExecutionPayment: optUintStrPtr(bc.MaxExecutionPayment), - MinBid: optUintStrPtr(bc.MinBid), - BuilderBoostFactor: optUintStrPtr(bc.BuilderBoostFactor), - } - for _, b := range bc.Builders { - out.Builders = append(out.Builders, builderEntryJSONFromConsensus(b)) + Enabled: bc.Enabled, + DefaultMinBid: optUintStrPtr(bc.MinBid), + DefaultBuilderBoostFactor: optUintStrPtr(bc.BuilderBoostFactor), + } + if bc.Builders != nil { + out.Builders = make([]*BuilderEntryJson, 0, len(bc.Builders)) + for _, b := range bc.Builders { + out.Builders = append(out.Builders, builderEntryJSONFromConsensus(b)) + } } return out } func builderEntryJSONFromConsensus(be *proposer.BuilderEntry) *BuilderEntryJson { out := &BuilderEntryJson{ - Url: be.URL, - Proxy: be.Proxy, MaxExecutionPayment: optUintStrPtr(be.MaxExecutionPayment), MinBid: optUintStrPtr(be.MinBid), BuilderBoostFactor: optUintStrPtr(be.BuilderBoostFactor), } + if be.URL != "" { + out.Url = strPtr(be.URL) + } if len(be.Pubkey) != 0 { - out.Pubkey = strPtr(hexutil.Encode(be.Pubkey)) + out.BuilderPubkey = strPtr(hexutil.Encode(be.Pubkey)) } if len(be.AuthData) != 0 { out.AuthData = strPtr(hexutil.Encode(be.AuthData)) @@ -221,119 +205,97 @@ func builderEntryJSONFromConsensus(be *proposer.BuilderEntry) *BuilderEntryJson return out } -// optionFromValidatorConfig builds a fresh Option from a submitted document. Every -// field is validated; unset fields stay nil so they inherit from default_config. -func optionFromValidatorConfig(cfg *ValidatorConfig) (*proposer.Option, error) { - opt := &proposer.Option{} - if cfg.FeeRecipient != nil { - if !common.IsHexAddress(*cfg.FeeRecipient) { - return nil, errors.Errorf("fee_recipient %s is not a valid execution address", *cfg.FeeRecipient) - } - opt.FeeRecipientConfig = &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress(*cfg.FeeRecipient)} - } - if cfg.TargetGasLimit != nil { - v, err := parseUint(*cfg.TargetGasLimit, "target_gas_limit") - if err != nil { - return nil, err - } - opt.GasLimit = v - } - if cfg.Graffiti != nil { - opt.GraffitiConfig = &proposer.GraffitiConfig{Graffiti: *cfg.Graffiti} - } - if cfg.Builder != nil { - bc, err := builderConfigFromJSON(cfg.Builder) - if err != nil { - return nil, err - } - opt.BuilderConfig = bc - } - return opt, nil -} - func builderConfigFromJSON(in *BuilderConfigJson) (*proposer.BuilderConfig, error) { - bc := &proposer.BuilderConfig{Enabled: in.Enabled, Proxy: in.Proxy} - if in.MaxExecutionPayment != nil { - v, err := parseUint(*in.MaxExecutionPayment, "builder.max_execution_payment") - if err != nil { - return nil, err - } - bc.MaxExecutionPayment = &v - } - if in.MinBid != nil { - v, err := parseUint(*in.MinBid, "builder.min_bid") + bc := &proposer.BuilderConfig{Enabled: in.Enabled} + if in.DefaultMinBid != nil { + v, err := parseUint(*in.DefaultMinBid, "default_min_bid") if err != nil { return nil, err } bc.MinBid = &v } - if in.BuilderBoostFactor != nil { - v, err := parseUint(*in.BuilderBoostFactor, "builder.builder_boost_factor") + if in.DefaultBuilderBoostFactor != nil { + v, err := parseUint(*in.DefaultBuilderBoostFactor, "default_builder_boost_factor") if err != nil { return nil, err } bc.BuilderBoostFactor = &v } + if in.Builders == nil { + return bc, nil + } + if len(in.Builders) > maxBuilderEntries { + return nil, errors.Errorf("builders exceeds %d entries", maxBuilderEntries) + } + // Non-nil (possibly empty) list means "use exactly these builders", not "inherit". + bc.Builders = make([]*proposer.BuilderEntry, 0, len(in.Builders)) seen := make(map[string]bool, len(in.Builders)) + seenPubkey := make(map[string]bool, len(in.Builders)) for i, entry := range in.Builders { be, err := builderEntryFromJSON(entry, i) if err != nil { return nil, err } - if seen[be.URL] { - return nil, errors.Errorf("builder.builders[%d]: two builder entries share the same url", i) + tuple := fmt.Sprintf("%s|%x|%x", be.URL, be.AuthData, be.Pubkey) + if seen[tuple] { + return nil, errors.Errorf("builders[%d]: duplicate url, auth_data and builder_pubkey", i) + } + seen[tuple] = true + if len(be.Pubkey) != 0 { + if seenPubkey[string(be.Pubkey)] { + return nil, errors.Errorf("builders[%d]: builder_pubkey appears on more than one entry", i) + } + seenPubkey[string(be.Pubkey)] = true } - seen[be.URL] = true bc.Builders = append(bc.Builders, be) } return bc, nil } func builderEntryFromJSON(in *BuilderEntryJson, i int) (*proposer.BuilderEntry, error) { - if in.Url == "" { - return nil, errors.Errorf("builder.builders[%d].url is required", i) - } - if u, err := url.Parse(in.Url); err != nil || u.Scheme == "" || u.Host == "" { - return nil, errors.Errorf("builder.builders[%d].url is not a valid URL", i) - } - if in.AuthData == nil { - log.WithField("builder", logs.MaskCredentialsLogging(in.Url)).Warn( - "Builder entry has no auth_data; builders requiring an out-of-band agreement will reject its requests") + be := &proposer.BuilderEntry{} + if in.Url != nil && *in.Url != "" { + if u, err := url.Parse(*in.Url); err != nil || u.Scheme == "" || u.Host == "" { + return nil, errors.Errorf("builders[%d].url is not a valid URL", i) + } + be.URL = *in.Url } - be := &proposer.BuilderEntry{URL: in.Url, Proxy: in.Proxy} - if in.Pubkey != nil { - pk, err := hexutil.Decode(*in.Pubkey) + if in.BuilderPubkey != nil { + pk, err := hexutil.Decode(*in.BuilderPubkey) if err != nil || len(pk) != fieldparams.BLSPubkeyLength { - return nil, errors.Errorf("builder.builders[%d].pubkey is not a valid BLS public key", i) + return nil, errors.Errorf("builders[%d].builder_pubkey is not a valid BLS public key", i) } be.Pubkey = pk } + if be.URL == "" && len(be.Pubkey) == 0 { + return nil, errors.Errorf("builders[%d]: at least one of url and builder_pubkey is required", i) + } if in.AuthData != nil { ad, err := hexutil.Decode(*in.AuthData) if err != nil { - return nil, errors.Errorf("builder.builders[%d].auth_data is not valid hex", i) + return nil, errors.Errorf("builders[%d].auth_data is not valid hex", i) } if len(ad) > 4096 { - return nil, errors.Errorf("builder.builders[%d].auth_data exceeds 4096 bytes", i) + return nil, errors.Errorf("builders[%d].auth_data exceeds 4096 bytes", i) } be.AuthData = ad } if in.MaxExecutionPayment != nil { - v, err := parseUint(*in.MaxExecutionPayment, "builder.builders[].max_execution_payment") + v, err := parseUint(*in.MaxExecutionPayment, "builders[].max_execution_payment") if err != nil { return nil, err } be.MaxExecutionPayment = &v } if in.MinBid != nil { - v, err := parseUint(*in.MinBid, "builder.builders[].min_bid") + v, err := parseUint(*in.MinBid, "builders[].min_bid") if err != nil { return nil, err } be.MinBid = &v } if in.BuilderBoostFactor != nil { - v, err := parseUint(*in.BuilderBoostFactor, "builder.builders[].builder_boost_factor") + v, err := parseUint(*in.BuilderBoostFactor, "builders[].builder_boost_factor") if err != nil { return nil, err } diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index 859098e0f39d..fa5ff1f89535 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -5,16 +5,15 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "strconv" + "strings" "testing" "github.com/OffchainLabs/prysm/v7/config/proposer" - "github.com/OffchainLabs/prysm/v7/testing/assert" "github.com/OffchainLabs/prysm/v7/testing/require" "github.com/OffchainLabs/prysm/v7/validator/keymanager/derived" mocks "github.com/OffchainLabs/prysm/v7/validator/testing" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "google.golang.org/protobuf/proto" ) // setupConfigServer builds a Server with a derived keymanager holding numKeys @@ -33,293 +32,181 @@ func setupConfigServer(t *testing.T, numKeys int) (*Server, [][48]byte) { return srv, keys } -func postValidatorConfig(t *testing.T, s *Server, rawBody string) *httptest.ResponseRecorder { - req := httptest.NewRequest(http.MethodPost, "/eth/v1/validator/config", bytes.NewBufferString(rawBody)) +func postBuilders(t *testing.T, s *Server, pubkey, body string) *httptest.ResponseRecorder { + req := httptest.NewRequest(http.MethodPost, "/eth/v1/validator/"+pubkey+"/builders", bytes.NewBufferString(body)) + req.SetPathValue("pubkey", pubkey) w := httptest.NewRecorder() w.Body = &bytes.Buffer{} - s.SetValidatorConfig(w, req) + s.SetBuilders(w, req) return w } -func getValidatorConfig(t *testing.T, s *Server, query string) *GetValidatorConfigResponse { - req := httptest.NewRequest(http.MethodGet, "/eth/v1/validator/config"+query, nil) +func getBuilders(t *testing.T, s *Server, pubkey string) (*httptest.ResponseRecorder, *BuilderConfigJson) { + req := httptest.NewRequest(http.MethodGet, "/eth/v1/validator/"+pubkey+"/builders", nil) + req.SetPathValue("pubkey", pubkey) w := httptest.NewRecorder() w.Body = &bytes.Buffer{} - s.GetValidatorConfig(w, req) - require.Equal(t, http.StatusOK, w.Code) - resp := &GetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), resp)) - return resp + s.GetBuilders(w, req) + cfg := &BuilderConfigJson{} + if w.Code == http.StatusOK { + require.NoError(t, json.Unmarshal(w.Body.Bytes(), cfg)) + } + return w, cfg } -func TestServer_SetGetValidatorConfig_FullDocument(t *testing.T) { - srv, keys := setupConfigServer(t, 1) - // Seed a read-only default_config so GET surfaces it alongside the per-key fragment. - require.NoError(t, srv.validatorService.SetProposerSettings(t.Context(), &proposer.Settings{ - Version: proposer.SchemaV2, - DefaultConfig: &proposer.Option{FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress("0x1111111111111111111111111111111111111111")}}, - })) - - pk := hexutil.Encode(keys[0][:]) - feeRecipient := "0xabcf8e0d4e9587369b2301d0790347320302cc09" - body := `{"configs":{"` + pk + `":{` + - `"fee_recipient":"` + feeRecipient + `",` + - `"target_gas_limit":"30000000",` + - `"graffiti":"hello world",` + - `"builder":{"enabled":true,"builders":[{"url":"http://relay.example"}],` + - `"max_execution_payment":"1000000","min_bid":"5","builder_boost_factor":"150"}` + - `}}}` - - w := postValidatorConfig(t, srv, body) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.NotNil(t, setResp.Data[pk]) - require.Equal(t, configStatusSet, setResp.Data[pk].Status) - - getResp := getValidatorConfig(t, srv, "") - require.NotNil(t, getResp.Data.DefaultConfig) - require.NotNil(t, getResp.Data.DefaultConfig.FeeRecipient) - require.Equal(t, common.HexToAddress("0x1111111111111111111111111111111111111111"), common.HexToAddress(*getResp.Data.DefaultConfig.FeeRecipient)) - - cfg := getResp.Data.Configs[pk] - require.NotNil(t, cfg) - require.NotNil(t, cfg.FeeRecipient) - require.Equal(t, common.HexToAddress(feeRecipient), common.HexToAddress(*cfg.FeeRecipient)) - require.NotNil(t, cfg.TargetGasLimit) - require.Equal(t, "30000000", *cfg.TargetGasLimit) - require.NotNil(t, cfg.Graffiti) - require.Equal(t, "hello world", *cfg.Graffiti) - require.NotNil(t, cfg.Builder) - require.NotNil(t, cfg.Builder.Enabled) - require.Equal(t, true, *cfg.Builder.Enabled) - require.Equal(t, 1, len(cfg.Builder.Builders)) - require.Equal(t, "http://relay.example", cfg.Builder.Builders[0].Url) - require.NotNil(t, cfg.Builder.MaxExecutionPayment) - require.Equal(t, "1000000", *cfg.Builder.MaxExecutionPayment) - require.NotNil(t, cfg.Builder.MinBid) - require.Equal(t, "5", *cfg.Builder.MinBid) - require.NotNil(t, cfg.Builder.BuilderBoostFactor) - require.Equal(t, "150", *cfg.Builder.BuilderBoostFactor) +func deleteBuilders(t *testing.T, s *Server, pubkey string) *httptest.ResponseRecorder { + req := httptest.NewRequest(http.MethodDelete, "/eth/v1/validator/"+pubkey+"/builders", nil) + req.SetPathValue("pubkey", pubkey) + w := httptest.NewRecorder() + w.Body = &bytes.Buffer{} + s.DeleteBuilders(w, req) + return w } -func TestServer_SetValidatorConfig_FullReplacementNotMerge(t *testing.T) { +func TestServer_SetGetBuilders_RoundTrip(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) - feeRecipient := "0xabcf8e0d4e9587369b2301d0790347320302cc09" - - w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"fee_recipient":"`+feeRecipient+`","graffiti":"first"}}}`) - require.Equal(t, http.StatusOK, w.Code) - first := getValidatorConfig(t, srv, "") - require.NotNil(t, first.Data.Configs[pk].Graffiti) - - // Re-POST with only fee_recipient; the whole fragment is replaced, so graffiti disappears. - w = postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"fee_recipient":"`+feeRecipient+`"}}}`) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusSet, setResp.Data[pk].Status) - - second := getValidatorConfig(t, srv, "") - cfg := second.Data.Configs[pk] - require.NotNil(t, cfg) - require.NotNil(t, cfg.FeeRecipient) - require.IsNil(t, cfg.Graffiti) + body := `{"enabled":true,"default_min_bid":"5","default_builder_boost_factor":"120",` + + `"builders":[{"url":"https://b.example","auth_data":"0x0102","max_execution_payment":"1000"}]}` + + w := postBuilders(t, srv, pk, body) + require.Equal(t, http.StatusAccepted, w.Code) + + _, cfg := getBuilders(t, srv, pk) + require.NotNil(t, cfg.Enabled) + require.Equal(t, true, *cfg.Enabled) + require.Equal(t, "5", *cfg.DefaultMinBid) + require.Equal(t, "120", *cfg.DefaultBuilderBoostFactor) + require.Equal(t, 1, len(cfg.Builders)) + require.Equal(t, "https://b.example", *cfg.Builders[0].Url) + require.Equal(t, "1000", *cfg.Builders[0].MaxExecutionPayment) + // Resolved: the entry omitted min_bid/boost, so GET fills them from the defaults. + require.Equal(t, "5", *cfg.Builders[0].MinBid) + require.Equal(t, "120", *cfg.Builders[0].BuilderBoostFactor) } -func TestServer_SetValidatorConfig_EmptyDocumentClearsKey(t *testing.T) { +func TestServer_SetBuilders_FullReplace(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) - w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"graffiti":"present"}}}`) - require.Equal(t, http.StatusOK, w.Code) - require.NotNil(t, getValidatorConfig(t, srv, "").Data.Configs[pk]) - - w = postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{}}}`) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusSet, setResp.Data[pk].Status) + require.Equal(t, http.StatusAccepted, postBuilders(t, srv, pk, + `{"enabled":true,"builders":[{"url":"https://a.example"},{"url":"https://b.example"}]}`).Code) + require.Equal(t, http.StatusAccepted, postBuilders(t, srv, pk, + `{"enabled":true,"builders":[{"url":"https://c.example"}]}`).Code) - _, present := getValidatorConfig(t, srv, "").Data.Configs[pk] - require.Equal(t, false, present) + _, cfg := getBuilders(t, srv, pk) + require.Equal(t, 1, len(cfg.Builders)) + require.Equal(t, "https://c.example", *cfg.Builders[0].Url) } -// Configs for keys the server does not yet manage are stored (pre-provisioning -// per the spec) and take effect when the key is later added. -func TestServer_SetValidatorConfig_UnknownPubkeyPreProvisions(t *testing.T) { - srv, _ := setupConfigServer(t, 1) - // 48-byte pubkey never recovered into the keymanager. - unknown := "0x" + "abcdef0011223344556677889900aabbccddeeff00112233445566778899aabbccddeeff001122334455667788990011" - - w := postValidatorConfig(t, srv, `{"configs":{"`+unknown+`":{"graffiti":"x"}}}`) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.NotNil(t, setResp.Data[unknown]) - require.Equal(t, configStatusSet, setResp.Data[unknown].Status) +func TestServer_SetBuilders_EmptyArrayIsUseNone(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + require.Equal(t, http.StatusAccepted, postBuilders(t, srv, pk, `{"enabled":true,"builders":[]}`).Code) - getResp := getValidatorConfig(t, srv, "") - cfg := getResp.Data.Configs[unknown] - require.NotNil(t, cfg) - require.NotNil(t, cfg.Graffiti) - require.Equal(t, "x", *cfg.Graffiti) + _, cfg := getBuilders(t, srv, pk) + require.NotNil(t, cfg.Builders) + require.Equal(t, 0, len(cfg.Builders)) } -// Unrecognized fields are ignored for forward compatibility, per the spec. -func TestServer_SetValidatorConfig_UnknownJSONFieldIgnored(t *testing.T) { +func TestServer_SetBuilders_BuilderPubkeyOnlyEntry(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) - - // Unknown fields in the per-key document and the envelope are both ignored; - // recognized fields alongside them are still applied. - w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"not_a_field":"x","graffiti":"kept"}},"bogus":true}`) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusSet, setResp.Data[pk].Status) - - getResp := getValidatorConfig(t, srv, "") - require.NotNil(t, getResp.Data.Configs[pk]) - require.Equal(t, "kept", *getResp.Data.Configs[pk].Graffiti) + bpk := "0x" + strings.Repeat("ab", 48) + body := `{"enabled":true,"builders":[{"builder_pubkey":"` + bpk + `","min_bid":"9"}]}` + + require.Equal(t, http.StatusAccepted, postBuilders(t, srv, pk, body).Code) + _, cfg := getBuilders(t, srv, pk) + require.Equal(t, 1, len(cfg.Builders)) + require.IsNil(t, cfg.Builders[0].Url) + require.Equal(t, bpk, *cfg.Builders[0].BuilderPubkey) } -func TestServer_SetValidatorConfig_PerKeyErrorsIsolated(t *testing.T) { - srv, keys := setupConfigServer(t, 2) - good := hexutil.Encode(keys[0][:]) - badFee := hexutil.Encode(keys[1][:]) - - body := `{"configs":{` + - `"` + good + `":{"graffiti":"ok"},` + - `"` + badFee + `":{"fee_recipient":"0xnothex"}` + - `}}` - w := postValidatorConfig(t, srv, body) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - - require.Equal(t, configStatusSet, setResp.Data[good].Status) - require.Equal(t, configStatusError, setResp.Data[badFee].Status) - require.NotEqual(t, "", setResp.Data[badFee].Message) - - getResp := getValidatorConfig(t, srv, "") - require.NotNil(t, getResp.Data.Configs[good]) - _, badPresent := getResp.Data.Configs[badFee] - require.Equal(t, false, badPresent) - - // A non-uint target_gas_limit is likewise a per-key error. - w = postValidatorConfig(t, srv, `{"configs":{"`+good+`":{"target_gas_limit":"notanumber"}}}`) - require.Equal(t, http.StatusOK, w.Code) - setResp = &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusError, setResp.Data[good].Status) - require.NotEqual(t, "", setResp.Data[good].Message) +func TestServer_SetBuilders_Rejects(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + bpk := "0x" + strings.Repeat("ab", 48) + + cases := map[string]struct { + body, contains string + }{ + "missing enabled": {`{"builders":[{"url":"https://a"}]}`, "enabled is required"}, + "entry has neither": {`{"enabled":true,"builders":[{"min_bid":"1"}]}`, "at least one of url and builder_pubkey"}, + "duplicate tuple": {`{"enabled":true,"builders":[{"url":"https://a"},{"url":"https://a"}]}`, "duplicate url, auth_data and builder_pubkey"}, + "builder_pubkey repeated": {`{"enabled":true,"builders":[{"builder_pubkey":"` + bpk + `"},{"url":"https://a","builder_pubkey":"` + bpk + `"}]}`, "builder_pubkey appears on more than one"}, + "invalid url": {`{"enabled":true,"builders":[{"url":"not a url"}]}`, "url is not a valid URL"}, + } + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + w := postBuilders(t, srv, pk, tc.body) + require.Equal(t, http.StatusBadRequest, w.Code) + require.Equal(t, true, strings.Contains(w.Body.String(), tc.contains), "body: %s", w.Body.String()) + }) + } } -func TestServer_GetValidatorConfig_PubkeyFilter(t *testing.T) { - srv, keys := setupConfigServer(t, 2) - pk0 := hexutil.Encode(keys[0][:]) - pk1 := hexutil.Encode(keys[1][:]) - - w := postValidatorConfig(t, srv, `{"configs":{"`+pk0+`":{"graffiti":"a"},"`+pk1+`":{"graffiti":"b"}}}`) - require.Equal(t, http.StatusOK, w.Code) - - all := getValidatorConfig(t, srv, "") - require.Equal(t, 2, len(all.Data.Configs)) - - filtered := getValidatorConfig(t, srv, "?pubkeys="+pk0) - require.Equal(t, 1, len(filtered.Data.Configs)) - require.NotNil(t, filtered.Data.Configs[pk0]) - _, present := filtered.Data.Configs[pk1] - require.Equal(t, false, present) +// Same url with distinct auth_data is a legal pair, not a duplicate. +func TestServer_SetBuilders_SharedURLDistinctAuth(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + body := `{"enabled":true,"builders":[{"url":"https://a","auth_data":"0x01"},{"url":"https://a","auth_data":"0x02"}]}` + require.Equal(t, http.StatusAccepted, postBuilders(t, srv, pk, body).Code) + _, cfg := getBuilders(t, srv, pk) + require.Equal(t, 2, len(cfg.Builders)) } -func TestServer_SetValidatorConfig_ValidatorServiceNil(t *testing.T) { - s := &Server{} - w := postValidatorConfig(t, s, `{"configs":{}}`) - assert.Equal(t, http.StatusServiceUnavailable, w.Code) +func TestServer_SetBuilders_MaxEntries(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + entries := make([]string, 0, maxBuilderEntries+1) + for i := 0; i <= maxBuilderEntries; i++ { + entries = append(entries, `{"url":"https://b`+strconv.Itoa(i)+`.example"}`) + } + body := `{"enabled":true,"builders":[` + strings.Join(entries, ",") + `]}` + w := postBuilders(t, srv, pk, body) + require.Equal(t, http.StatusBadRequest, w.Code) + require.Equal(t, true, strings.Contains(w.Body.String(), "exceeds 64 entries")) } -// Pins the presence semantics of max_execution_payment at the API layer: -// explicit "0" (trustless-only) round-trips; unset stays absent from GET. -func TestServer_SetGetValidatorConfig_MaxExecutionPaymentZeroVsAbsent(t *testing.T) { - srv, keys := setupConfigServer(t, 2) - pkZero := hexutil.Encode(keys[0][:]) - pkAbsent := hexutil.Encode(keys[1][:]) +func TestServer_DeleteBuilders(t *testing.T) { + srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) - body := `{"configs":{` + - `"` + pkZero + `":{"builder":{"enabled":true,"builders":[{"url":"http://a.example"}],"max_execution_payment":"0"}},` + - `"` + pkAbsent + `":{"builder":{"enabled":true,"builders":[{"url":"http://b.example"}]}}` + - `}}` - w := postValidatorConfig(t, srv, body) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusSet, setResp.Data[pkZero].Status) - require.Equal(t, configStatusSet, setResp.Data[pkAbsent].Status) + // Nothing configured yet -> 404. + require.Equal(t, http.StatusNotFound, deleteBuilders(t, srv, pk).Code) - getResp := getValidatorConfig(t, srv, "") - zero := getResp.Data.Configs[pkZero] - require.NotNil(t, zero) - require.NotNil(t, zero.Builder) - require.NotNil(t, zero.Builder.MaxExecutionPayment) - require.Equal(t, "0", *zero.Builder.MaxExecutionPayment) + require.Equal(t, http.StatusAccepted, postBuilders(t, srv, pk, `{"enabled":true,"builders":[{"url":"https://a"}]}`).Code) + require.Equal(t, http.StatusNoContent, deleteBuilders(t, srv, pk).Code) - absent := getResp.Data.Configs[pkAbsent] - require.NotNil(t, absent) - require.NotNil(t, absent.Builder) - assert.Equal(t, (*string)(nil), absent.Builder.MaxExecutionPayment) + // After delete the key follows defaults (no per-key builders). + _, cfg := getBuilders(t, srv, pk) + require.NotNil(t, cfg.Enabled) + require.Equal(t, false, *cfg.Enabled) + require.Equal(t, 0, len(cfg.Builders)) } -// v1 settings keep the gas limit on the builder config; GET must still surface it -// as target_gas_limit rather than dropping it. -func TestServer_GetValidatorConfig_V1BuilderGasLimit(t *testing.T) { +// GET on an unconfigured key resolves against default_config. +func TestServer_GetBuilders_ResolvesDefault(t *testing.T) { srv, keys := setupConfigServer(t, 1) + pk := hexutil.Encode(keys[0][:]) + enabled := true require.NoError(t, srv.validatorService.SetProposerSettings(t.Context(), &proposer.Settings{ - Version: proposer.SchemaV1, - ProposeConfig: map[[48]byte]*proposer.Option{ - keys[0]: { - FeeRecipientConfig: &proposer.FeeRecipientConfig{FeeRecipient: common.HexToAddress("0xabcf8e0d4e9587369b2301d0790347320302cc09")}, - BuilderConfig: &proposer.BuilderConfig{Enabled: proto.Bool(true), GasLimit: 40000000}, - }, + Version: proposer.SchemaV2, + DefaultConfig: &proposer.Option{ + BuilderConfig: &proposer.BuilderConfig{Enabled: &enabled, Builders: []*proposer.BuilderEntry{{URL: "https://default.example"}}}, }, })) - pk := hexutil.Encode(keys[0][:]) - cfg := getValidatorConfig(t, srv, "").Data.Configs[pk] - require.NotNil(t, cfg) - require.NotNil(t, cfg.TargetGasLimit) - require.Equal(t, "40000000", *cfg.TargetGasLimit) + _, cfg := getBuilders(t, srv, pk) + require.NotNil(t, cfg.Enabled) + require.Equal(t, true, *cfg.Enabled) + require.Equal(t, 1, len(cfg.Builders)) + require.Equal(t, "https://default.example", *cfg.Builders[0].Url) } -func TestServer_SetValidatorConfig_DuplicateBuilderURL(t *testing.T) { +func TestServer_SetBuilders_ValidatorServiceNil(t *testing.T) { srv, keys := setupConfigServer(t, 1) - pk := hexutil.Encode(keys[0][:]) - - body := `{"configs":{"` + pk + `":{"builder":{"enabled":true,"builders":[{"url":"https://b.example"},{"url":"https://b.example","min_bid":"5"}]}}}}` - w := postValidatorConfig(t, srv, body) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusError, setResp.Data[pk].Status) - assert.StringContains(t, "share the same url", setResp.Data[pk].Message) - - getResp := getValidatorConfig(t, srv, "") - require.Equal(t, 0, len(getResp.Data.Configs)) -} - -func TestServer_SetValidatorConfig_InvalidBuilderURL(t *testing.T) { - srv, keys := setupConfigServer(t, 1) - pk := hexutil.Encode(keys[0][:]) - - w := postValidatorConfig(t, srv, `{"configs":{"`+pk+`":{"builder":{"enabled":true,"builders":[{"url":"not a url at all"}]}}}}`) - require.Equal(t, http.StatusOK, w.Code) - setResp := &SetValidatorConfigResponse{} - require.NoError(t, json.Unmarshal(w.Body.Bytes(), setResp)) - require.Equal(t, configStatusError, setResp.Data[pk].Status) - assert.StringContains(t, "url is not a valid URL", setResp.Data[pk].Message) - - // Nothing persisted for the key. - getResp := getValidatorConfig(t, srv, "") - require.Equal(t, 0, len(getResp.Data.Configs)) + srv.validatorService = nil + w := postBuilders(t, srv, hexutil.Encode(keys[0][:]), `{"enabled":true}`) + require.Equal(t, http.StatusServiceUnavailable, w.Code) } diff --git a/validator/rpc/server.go b/validator/rpc/server.go index 0f7fc30eec57..52ec2e37a92b 100644 --- a/validator/rpc/server.go +++ b/validator/rpc/server.go @@ -200,8 +200,9 @@ func (s *Server) InitializeRoutes() error { s.router.HandleFunc("GET /eth/v1/validator/{pubkey}/graffiti", s.GetGraffiti) s.router.HandleFunc("POST /eth/v1/validator/{pubkey}/graffiti", s.SetGraffiti) s.router.HandleFunc("DELETE /eth/v1/validator/{pubkey}/graffiti", s.DeleteGraffiti) - s.router.HandleFunc("GET /eth/v1/validator/config", s.GetValidatorConfig) - s.router.HandleFunc("POST /eth/v1/validator/config", s.SetValidatorConfig) + s.router.HandleFunc("GET /eth/v1/validator/{pubkey}/builders", s.GetBuilders) + s.router.HandleFunc("POST /eth/v1/validator/{pubkey}/builders", s.SetBuilders) + s.router.HandleFunc("DELETE /eth/v1/validator/{pubkey}/builders", s.DeleteBuilders) // auth endpoint s.router.HandleFunc("GET "+api.WebUrlPrefix+"initialize", s.Initialize) diff --git a/validator/rpc/structs.go b/validator/rpc/structs.go index c4436b985171..32bd52852c15 100644 --- a/validator/rpc/structs.go +++ b/validator/rpc/structs.go @@ -109,51 +109,24 @@ type GraffitiData struct { Graffiti string `json:"graffiti"` } -// Validator config keymanager api (keymanager-APIs #87). All integers are -// decimal strings and all byte values are 0x-prefixed hex, per keymanager conventions. -type GetValidatorConfigResponse struct { - Data *ValidatorConfigData `json:"data"` -} - -type ValidatorConfigData struct { - DefaultConfig *ValidatorConfig `json:"default_config,omitempty"` - Configs map[string]*ValidatorConfig `json:"configs"` -} - -type SetValidatorConfigRequest struct { - Configs map[string]*ValidatorConfig `json:"configs"` -} - -type SetValidatorConfigResponse struct { - Data map[string]*ValidatorConfigStatus `json:"data"` -} - -type ValidatorConfigStatus struct { - Status string `json:"status"` - Message string `json:"message,omitempty"` -} - -type ValidatorConfig struct { - FeeRecipient *string `json:"fee_recipient,omitempty"` - TargetGasLimit *string `json:"target_gas_limit,omitempty"` - Graffiti *string `json:"graffiti,omitempty"` - Builder *BuilderConfigJson `json:"builder,omitempty"` -} - +// Per-key builders keymanager api (keymanager-APIs #88). All integers are decimal +// strings and all byte values are 0x-prefixed hex, per keymanager conventions. +// A nil Builders means "use the validator client's builders"; a non-nil empty +// Builders means "use none" (p2p bids only). +// Builders has no omitempty: on the resolved GET response an empty list ("use no +// builders") must serialize as [] rather than be dropped. On POST input an absent +// field still decodes to nil, which means "inherit the validator client's builders". type BuilderConfigJson struct { - Enabled *bool `json:"enabled,omitempty"` - Builders []*BuilderEntryJson `json:"builders,omitempty"` - MaxExecutionPayment *string `json:"max_execution_payment,omitempty"` - MinBid *string `json:"min_bid,omitempty"` - BuilderBoostFactor *string `json:"builder_boost_factor,omitempty"` - Proxy *string `json:"proxy,omitempty"` + Enabled *bool `json:"enabled"` + Builders []*BuilderEntryJson `json:"builders"` + DefaultMinBid *string `json:"default_min_bid,omitempty"` + DefaultBuilderBoostFactor *string `json:"default_builder_boost_factor,omitempty"` } type BuilderEntryJson struct { - Url string `json:"url"` - Pubkey *string `json:"pubkey,omitempty"` + Url *string `json:"url,omitempty"` AuthData *string `json:"auth_data,omitempty"` - Proxy *string `json:"proxy,omitempty"` + BuilderPubkey *string `json:"builder_pubkey,omitempty"` MaxExecutionPayment *string `json:"max_execution_payment,omitempty"` MinBid *string `json:"min_bid,omitempty"` BuilderBoostFactor *string `json:"builder_boost_factor,omitempty"` From 967744a1051b57a5f090232afaf785bde78d7986 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Mon, 27 Jul 2026 10:52:11 -0500 Subject: [PATCH 18/19] adding builder api changes --- api/client/builder/client_gloas.go | 56 +- api/client/builder/client_gloas_test.go | 41 +- api/headers.go | 2 + .../rpc/eth/validator/builder_preferences.go | 39 +- .../eth/validator/builder_preferences_test.go | 21 +- .../james-prysm_validator-config-endpoint.md | 2 + .../v1alpha1/gloas_builder_api.minimal.ssz.go | 675 ++++++++++++++++++ proto/prysm/v1alpha1/gloas_builder_api.proto | 2 +- proto/prysm/v1alpha1/gloas_builder_api.ssz.go | 2 + 9 files changed, 777 insertions(+), 63 deletions(-) create mode 100644 proto/prysm/v1alpha1/gloas_builder_api.minimal.ssz.go diff --git a/api/client/builder/client_gloas.go b/api/client/builder/client_gloas.go index 2f91bb675b17..3bcad6c70cd3 100644 --- a/api/client/builder/client_gloas.go +++ b/api/client/builder/client_gloas.go @@ -6,7 +6,9 @@ import ( "encoding/json" "fmt" "net/http" + "strconv" "strings" + "time" "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server/structs" @@ -57,34 +59,38 @@ func (c *Client) getExecutionPayloadBid( auth *ethpb.SignedRequestAuthV1, ssz bool, ) (*ethpb.SignedExecutionPayloadBid, error) { - accept := api.JsonMediaType + // builder-specs #165: the signed auth body is required and RequestAuthV1 is not + // fork-versioned, so no Eth-Consensus-Version header is sent on the request. + if auth == nil { + return nil, errors.New("request auth is required for the bid request") + } + accept, contentType := api.JsonMediaType, api.JsonMediaType if ssz { - accept = api.OctetStreamMediaType + accept, contentType = api.OctetStreamMediaType, api.OctetStreamMediaType } var body []byte + var err error + if ssz { + body, err = auth.MarshalSSZ() + } else { + body, err = json.Marshal(structs.SignedRequestAuthFromConsensus(auth)) + } + if err != nil { + return nil, errors.Wrap(err, "could not encode SignedRequestAuthV1") + } + now := time.Now() opts := []reqOption{func(r *http.Request) { r.Header.Set("Accept", accept) - r.Header.Set(api.VersionHeader, version.String(version.Gloas)) - }} - if auth != nil { - var err error - contentType := api.JsonMediaType - if ssz { - contentType = api.OctetStreamMediaType - body, err = auth.MarshalSSZ() - if err != nil { - return nil, errors.Wrap(err, "could not ssz encode SignedRequestAuthV1") - } - } else { - body, err = json.Marshal(structs.SignedRequestAuthFromConsensus(auth)) - if err != nil { - return nil, errors.Wrap(err, "could not json encode SignedRequestAuthV1") + r.Header.Set("Content-Type", contentType) + r.Header.Set(api.DateMillisecondsHeader, strconv.FormatInt(now.UnixMilli(), 10)) + if deadline, ok := ctx.Deadline(); ok { + ms := deadline.Sub(now).Milliseconds() + if ms < 0 { + ms = 0 } + r.Header.Set(api.TimeoutMillisecondsHeader, strconv.FormatInt(ms, 10)) } - opts = append(opts, func(r *http.Request) { - r.Header.Set("Content-Type", contentType) - }) - } + }} path := executionPayloadBidPath(slot, parentHash, parentRoot, proposerPubkey) raw, status, header, err := c.doWithStatus(ctx, http.MethodPost, path, bytes.NewReader(body), []int{http.StatusOK, http.StatusNoContent}, opts...) @@ -94,9 +100,9 @@ func (c *Client) getExecutionPayloadBid( if status == http.StatusNoContent { return nil, nil } - contentType := header.Get("Content-Type") + respContentType := header.Get("Content-Type") switch { - case strings.Contains(contentType, api.JsonMediaType): + case strings.Contains(respContentType, api.JsonMediaType): resp := &struct { Data *structs.SignedExecutionPayloadBid `json:"data"` }{} @@ -107,14 +113,14 @@ func (c *Client) getExecutionPayloadBid( return nil, errors.New("nil data in json SignedExecutionPayloadBid response") } return resp.Data.ToConsensus() - case strings.Contains(contentType, api.OctetStreamMediaType): + case strings.Contains(respContentType, api.OctetStreamMediaType): bid := ðpb.SignedExecutionPayloadBid{} if err := bid.UnmarshalSSZ(raw); err != nil { return nil, errors.Wrap(err, "could not ssz decode SignedExecutionPayloadBid") } return bid, nil default: - return nil, errors.Errorf("builder returned status %d with unexpected Content-Type %q: %s", status, contentType, bodySnippet(raw)) + return nil, errors.Errorf("builder returned status %d with unexpected Content-Type %q: %s", status, respContentType, bodySnippet(raw)) } } diff --git a/api/client/builder/client_gloas_test.go b/api/client/builder/client_gloas_test.go index f93563c7ebbe..140b04446c95 100644 --- a/api/client/builder/client_gloas_test.go +++ b/api/client/builder/client_gloas_test.go @@ -2,11 +2,13 @@ package builder import ( "bytes" + "context" "encoding/json" "io" "net/http" "net/url" "testing" + "time" "github.com/OffchainLabs/prysm/v7/api" "github.com/OffchainLabs/prysm/v7/api/server/structs" @@ -59,6 +61,35 @@ func gloasBidClient(t *testing.T, status int, contentType string, body []byte) * return &Client{hc: hc, baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"}} } +// builder-specs #165: auth is required, and the bid request carries Date-Milliseconds +// and X-Timeout-Ms but not Eth-Consensus-Version. +func TestClient_GetExecutionPayloadBid_AuthAndHeaders(t *testing.T) { + var pubkey [48]byte + var parentHash, parentRoot [32]byte + + c := &Client{hc: &http.Client{}, baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"}} + _, err := c.GetExecutionPayloadBid(t.Context(), 1, parentHash, parentRoot, pubkey, nil) + require.ErrorContains(t, "request auth is required", err) + + var got http.Header + hc := &http.Client{Transport: roundtrip(func(r *http.Request) (*http.Response, error) { + got = r.Header + return &http.Response{StatusCode: http.StatusNoContent, Header: http.Header{}, Body: io.NopCloser(bytes.NewReader(nil)), Request: r}, nil + })} + c = &Client{hc: hc, baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"}} + ctx, cancel := context.WithTimeout(t.Context(), 800*time.Millisecond) + defer cancel() + _, err = c.GetExecutionPayloadBid(ctx, 1, parentHash, parentRoot, pubkey, testBidAuth()) + require.NoError(t, err) + require.NotEqual(t, "", got.Get(api.DateMillisecondsHeader)) + require.NotEqual(t, "", got.Get(api.TimeoutMillisecondsHeader)) + require.Equal(t, "", got.Get(api.VersionHeader)) +} + +func testBidAuth() *eth.SignedRequestAuthV1 { + return ð.SignedRequestAuthV1{Message: ð.RequestAuthV1{Data: []byte{1}, Slot: 1}, Signature: make([]byte, 96)} +} + func TestClient_GetExecutionPayloadBid(t *testing.T) { ctx := t.Context() slot := primitives.Slot(123) @@ -72,7 +103,7 @@ func TestClient_GetExecutionPayloadBid(t *testing.T) { }{Data: structs.SignedExecutionPayloadBidFromConsensus(want)}) require.NoError(t, err) c := gloasBidClient(t, http.StatusOK, api.JsonMediaType, body) - got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, nil) + got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, testBidAuth()) require.NoError(t, err) require.NotNil(t, got) require.Equal(t, want.Message.Slot, got.Message.Slot) @@ -84,7 +115,7 @@ func TestClient_GetExecutionPayloadBid(t *testing.T) { body, err := want.MarshalSSZ() require.NoError(t, err) c := gloasBidClient(t, http.StatusOK, api.OctetStreamMediaType, body) - got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, nil) + got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, testBidAuth()) require.NoError(t, err) require.NotNil(t, got) require.Equal(t, want.Message.Value, got.Message.Value) @@ -93,7 +124,7 @@ func TestClient_GetExecutionPayloadBid(t *testing.T) { t.Run("no bid", func(t *testing.T) { c := gloasBidClient(t, http.StatusNoContent, "", nil) - got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, nil) + got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, testBidAuth()) require.NoError(t, err) require.IsNil(t, got) }) @@ -101,7 +132,7 @@ func TestClient_GetExecutionPayloadBid(t *testing.T) { t.Run("unexpected content type errors with status and body", func(t *testing.T) { html := []byte("Buildoor") c := gloasBidClient(t, http.StatusOK, "text/html; charset=utf-8", html) - got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, nil) + got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, testBidAuth()) require.IsNil(t, got) require.ErrorContains(t, "unexpected Content-Type", err) require.ErrorContains(t, "text/html", err) @@ -161,7 +192,7 @@ func TestClient_GetExecutionPayloadBid(t *testing.T) { }), } c := &Client{hc: hc, baseURL: &url.URL{Host: "localhost:3500", Scheme: "http"}, sszEnabled: true} - got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, nil) + got, err := c.GetExecutionPayloadBid(ctx, slot, parentHash, parentRoot, pubkey, testBidAuth()) require.NoError(t, err) require.NotNil(t, got) require.Equal(t, 2, reqCount) diff --git a/api/headers.go b/api/headers.go index 7bb9be93809e..12c638c8c9d2 100644 --- a/api/headers.go +++ b/api/headers.go @@ -10,6 +10,8 @@ const ( ConsensusBlockValueHeader = "Eth-Consensus-Block-Value" ExecutionPayloadIncludedHeader = "Eth-Execution-Payload-Included" BlobDataIncludedHeader = "Eth-Blob-Data-Included" + DateMillisecondsHeader = "Date-Milliseconds" + TimeoutMillisecondsHeader = "X-Timeout-Ms" JsonMediaType = "application/json" OctetStreamMediaType = "application/octet-stream" EventStreamMediaType = "text/event-stream" diff --git a/beacon-chain/rpc/eth/validator/builder_preferences.go b/beacon-chain/rpc/eth/validator/builder_preferences.go index 64078f885952..3ff5dee22b04 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences.go @@ -20,18 +20,19 @@ import ( ) // builderEntryJson is one element of the produceBlockV4 POST body: a bare array -// of per-builder entries (beacon-APIs #630). proxy and pubkey are accepted but -// currently unused. +// of per-builder entries (beacon-APIs #630). type builderEntryJson struct { Url string `json:"url"` - Proxy string `json:"proxy,omitempty"` - Pubkey string `json:"pubkey,omitempty"` + Pubkey string `json:"builder_pubkey,omitempty"` Auth *signedAuthJson `json:"auth"` MaxExecutionPayment string `json:"max_execution_payment,omitempty"` MinBid string `json:"min_bid,omitempty"` BuilderBoostFactor string `json:"builder_boost_factor,omitempty"` } +// maxProduceBuilderEntries is MAX_BUILDER_ENTRIES from beacon-APIs #630. +const maxProduceBuilderEntries = 64 + type signedAuthJson struct { Message *requestAuthJson `json:"message"` Signature string `json:"signature"` @@ -63,6 +64,9 @@ func parseBuilderPreferencesJSON(r *http.Request) ([]*eth.BuilderPreferenceV1, e } return nil, errors.Wrap(err, "could not decode builder preferences") } + if len(entries) > maxProduceBuilderEntries { + return nil, errors.Errorf("builder preferences exceeds %d entries", maxProduceBuilderEntries) + } prefs := make([]*eth.BuilderPreferenceV1, 0, len(entries)) for _, p := range entries { conv, err := p.toConsensus() @@ -72,7 +76,7 @@ func parseBuilderPreferencesJSON(r *http.Request) ([]*eth.BuilderPreferenceV1, e } prefs = append(prefs, conv) } - return dedupeBuilderPreferences(prefs) + return prefs, nil } func parseBuilderPreferencesSSZ(r *http.Request) ([]*eth.BuilderPreferenceV1, error) { @@ -87,30 +91,19 @@ func parseBuilderPreferencesSSZ(r *http.Request) ([]*eth.BuilderPreferenceV1, er if err := list.UnmarshalSSZ(body); err != nil { return nil, errors.Wrap(err, "could not decode builder preferences") } - prefs := make([]*eth.BuilderPreferenceV1, 0) - for _, p := range eth.BuilderPreferencesFromSSZ(list) { + all := eth.BuilderPreferencesFromSSZ(list) + if len(all) > maxProduceBuilderEntries { + return nil, errors.Errorf("builder preferences exceeds %d entries", maxProduceBuilderEntries) + } + prefs := make([]*eth.BuilderPreferenceV1, 0, len(all)) + for _, p := range all { if p.Url == "" || p.Request == nil || p.Request.Auth == nil || p.Request.Auth.Message == nil { log.Debug("Ignoring malformed builder entry") continue } prefs = append(prefs, p) } - return dedupeBuilderPreferences(prefs) -} - -// dedupeBuilderPreferences enforces the unique-url rule: duplicates invalidate the -// whole request, unlike malformed entries which were already skipped. -func dedupeBuilderPreferences(prefs []*eth.BuilderPreferenceV1) ([]*eth.BuilderPreferenceV1, error) { - out := make([]*eth.BuilderPreferenceV1, 0, len(prefs)) - seen := make(map[string]bool, len(prefs)) - for _, p := range prefs { - if seen[p.Url] { - return nil, errors.Errorf("two builder entries share the same url") - } - seen[p.Url] = true - out = append(out, p) - } - return out, nil + return prefs, nil } func (p *builderEntryJson) toConsensus() (*eth.BuilderPreferenceV1, error) { diff --git a/beacon-chain/rpc/eth/validator/builder_preferences_test.go b/beacon-chain/rpc/eth/validator/builder_preferences_test.go index 3e0e4f1e39b4..cde76c303eff 100644 --- a/beacon-chain/rpc/eth/validator/builder_preferences_test.go +++ b/beacon-chain/rpc/eth/validator/builder_preferences_test.go @@ -45,8 +45,8 @@ func TestParseBuilderPreferencesBody(t *testing.T) { require.Equal(t, "https://b", prefs[0].Url) }) t.Run("pubkey is parsed and bad pubkeys are skipped", func(t *testing.T) { - withKey := `{"url":"https://k","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"pubkey":"0x` + repeatHex(48) + `"}` - badKey := `{"url":"https://short","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"pubkey":"0x0102"}` + withKey := `{"url":"https://k","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"builder_pubkey":"0x` + repeatHex(48) + `"}` + badKey := `{"url":"https://short","auth":{"message":{"data":"0x0102","slot":"7"},"signature":"0x` + repeatHex(96) + `"},"builder_pubkey":"0x0102"}` r := httptest.NewRequest("POST", "/", bytes.NewBufferString(`[`+withKey+`,`+badKey+`]`)) prefs, err := parseBuilderPreferencesBody(r) require.NoError(t, err) @@ -76,11 +76,13 @@ func repeatHex(n int) string { } // Two entries sharing a url invalidate the whole request, unlike malformed entries. -func TestParseBuilderPreferencesBody_DuplicateURL(t *testing.T) { +// beacon-APIs #630 dropped the unique-url rule on the produce body; duplicates are kept. +func TestParseBuilderPreferencesBody_DuplicateURLAllowed(t *testing.T) { entry := `{"url":"https://b","auth":{"message":{"data":"0x01","slot":"7"},"signature":"0x` + repeatHex(96) + `"}}` r := httptest.NewRequest("POST", "/", bytes.NewBufferString(`[`+entry+`,`+entry+`]`)) - _, err := parseBuilderPreferencesBody(r) - require.ErrorContains(t, "share the same url", err) + prefs, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 2, len(prefs)) } // An octet-stream body is decoded as SSZ, and the sentinels round-trip back to the @@ -115,8 +117,8 @@ func TestParseBuilderPreferencesBody_SSZ(t *testing.T) { require.Equal(t, primitives.Gwei(1000), got[0].Request.Preferences.GetMaxExecutionPayment()) } -// Duplicate urls in an SSZ body invalidate the whole request, matching JSON. -func TestParseBuilderPreferencesBody_SSZ_DuplicateURL(t *testing.T) { +// Duplicate urls in an SSZ body are kept, matching the JSON path. +func TestParseBuilderPreferencesBody_SSZ_DuplicateURLAllowed(t *testing.T) { entry := func() *eth.BuilderPreferenceV1 { return ð.BuilderPreferenceV1{ Url: "https://dup.example", @@ -130,8 +132,9 @@ func TestParseBuilderPreferencesBody_SSZ_DuplicateURL(t *testing.T) { require.NoError(t, err) r := httptest.NewRequest("POST", "/", bytes.NewBuffer(body)) r.Header.Set("Content-Type", api.OctetStreamMediaType) - _, err = parseBuilderPreferencesBody(r) - require.ErrorContains(t, "share the same url", err) + prefs, err := parseBuilderPreferencesBody(r) + require.NoError(t, err) + require.Equal(t, 2, len(prefs)) } func TestSubmitBuilderPreferencesHandler(t *testing.T) { diff --git a/changelog/james-prysm_validator-config-endpoint.md b/changelog/james-prysm_validator-config-endpoint.md index 6f40ef5d02cb..d3cfea206369 100644 --- a/changelog/james-prysm_validator-config-endpoint.md +++ b/changelog/james-prysm_validator-config-endpoint.md @@ -22,6 +22,8 @@ - Decode empty builder `auth_data`/`pubkey` identically to unset across both validator DB backends. - Mask credentials in builder-client error messages and trace spans so a builder URL with userinfo or a query token cannot leak. - Send the inline builder-preferences produce-block body as SSZ; the beacon node accepts SSZ or JSON. +- Send the required `Date-Milliseconds` and `X-Timeout-Ms` headers on builder bid requests and require the signed auth body (builder-specs #165); the request no longer carries `Eth-Consensus-Version`. +- Accept up to 64 builder entries on the produce-block body and no longer reject duplicate builder urls there (beacon-APIs #630); the entry builder key is named `builder_pubkey`. - Validate the builder url on the ahead-of-time preferences endpoint before dialing. - Preserve the winning builder url when unwrapping a stateless self-build produce response. diff --git a/proto/prysm/v1alpha1/gloas_builder_api.minimal.ssz.go b/proto/prysm/v1alpha1/gloas_builder_api.minimal.ssz.go new file mode 100644 index 000000000000..7759d47d5efd --- /dev/null +++ b/proto/prysm/v1alpha1/gloas_builder_api.minimal.ssz.go @@ -0,0 +1,675 @@ +//go:build minimal + +// Code generated by fastssz. DO NOT EDIT. +package eth + +import ( + github_com_OffchainLabs_prysm_v7_consensus_types_primitives "github.com/OffchainLabs/prysm/v7/consensus-types/primitives" + ssz "github.com/prysmaticlabs/fastssz" +) + +// MarshalSSZ ssz marshals the RequestAuthV1 object +func (r *RequestAuthV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(r) +} + +// MarshalSSZTo ssz marshals the RequestAuthV1 object to a target array +func (r *RequestAuthV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(12) + + // Offset (0) 'Data' + dst = ssz.WriteOffset(dst, offset) + offset += len(r.Data) + + // Field (1) 'Slot' + dst = ssz.MarshalUint(dst, r.Slot) + + // Field (0) 'Data' + if size := len(r.Data); size > 4096 { + err = ssz.ErrBytesLengthFn("--.Data", size, 4096) + return + } + dst = append(dst, r.Data...) + + return +} + +// UnmarshalSSZ ssz unmarshals the RequestAuthV1 object +func (r *RequestAuthV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 12 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Data' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 12 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Slot' + r.Slot = ssz.UnmarshallUint[github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Slot](buf[4:12]) + + // Field (0) 'Data' + { + buf = tail[o0:] + if len(buf) > 4096 { + return ssz.ErrBytesLength + } + if cap(r.Data) == 0 { + r.Data = make([]byte, 0, len(buf)) + } + r.Data = append(r.Data, buf...) + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the RequestAuthV1 object +func (r *RequestAuthV1) SizeSSZ() (size int) { + size = 12 + + // Field (0) 'Data' + size += len(r.Data) + + return +} + +// HashTreeRoot ssz hashes the RequestAuthV1 object +func (r *RequestAuthV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(r) +} + +// HashTreeRootWith ssz hashes the RequestAuthV1 object with a hasher +func (r *RequestAuthV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Data' + { + elemIndx := hh.Index() + byteLen := uint64(len(r.Data)) + if byteLen > 4096 { + err = ssz.ErrIncorrectListSize + return + } + hh.AppendBytes32(r.Data) + hh.MerkleizeWithMixin(elemIndx, byteLen, (4096+31)/32) + } + + // Field (1) 'Slot' + ssz.PutUint(hh, r.Slot) + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the SignedRequestAuthV1 object +func (s *SignedRequestAuthV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(s) +} + +// MarshalSSZTo ssz marshals the SignedRequestAuthV1 object to a target array +func (s *SignedRequestAuthV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(100) + + // Offset (0) 'Message' + dst = ssz.WriteOffset(dst, offset) + if s.Message == nil { + s.Message = new(RequestAuthV1) + } + offset += s.Message.SizeSSZ() + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("--.Signature", size, 96) + return + } + dst = append(dst, s.Signature...) + + // Field (0) 'Message' + if dst, err = s.Message.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the SignedRequestAuthV1 object +func (s *SignedRequestAuthV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 100 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Message' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 100 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Signature' + if cap(s.Signature) == 0 { + s.Signature = make([]byte, 0, len(buf[4:100])) + } + s.Signature = append(s.Signature, buf[4:100]...) + + // Field (0) 'Message' + { + buf = tail[o0:] + if s.Message == nil { + s.Message = new(RequestAuthV1) + } + if err = s.Message.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the SignedRequestAuthV1 object +func (s *SignedRequestAuthV1) SizeSSZ() (size int) { + size = 100 + + // Field (0) 'Message' + if s.Message == nil { + s.Message = new(RequestAuthV1) + } + size += s.Message.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the SignedRequestAuthV1 object +func (s *SignedRequestAuthV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(s) +} + +// HashTreeRootWith ssz hashes the SignedRequestAuthV1 object with a hasher +func (s *SignedRequestAuthV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Message' + if err = s.Message.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Signature' + if size := len(s.Signature); size != 96 { + err = ssz.ErrBytesLengthFn("--.Signature", size, 96) + return + } + hh.PutBytes(s.Signature) + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the BuilderPreferencesV1 object +func (b *BuilderPreferencesV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BuilderPreferencesV1 object to a target array +func (b *BuilderPreferencesV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + + // Field (0) 'MaxExecutionPayment' + dst = ssz.MarshalUint(dst, b.MaxExecutionPayment) + + return +} + +// UnmarshalSSZ ssz unmarshals the BuilderPreferencesV1 object +func (b *BuilderPreferencesV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size != 8 { + return ssz.ErrSize + } + + // Field (0) 'MaxExecutionPayment' + b.MaxExecutionPayment = ssz.UnmarshallUint[github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei](buf[0:8]) + + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BuilderPreferencesV1 object +func (b *BuilderPreferencesV1) SizeSSZ() (size int) { + size = 8 + return +} + +// HashTreeRoot ssz hashes the BuilderPreferencesV1 object +func (b *BuilderPreferencesV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BuilderPreferencesV1 object with a hasher +func (b *BuilderPreferencesV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'MaxExecutionPayment' + ssz.PutUint(hh, b.MaxExecutionPayment) + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the BuilderPreferencesRequestV1 object +func (b *BuilderPreferencesRequestV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(b) +} + +// MarshalSSZTo ssz marshals the BuilderPreferencesRequestV1 object to a target array +func (b *BuilderPreferencesRequestV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(12) + + // Field (0) 'Preferences' + if b.Preferences == nil { + b.Preferences = new(BuilderPreferencesV1) + } + if dst, err = b.Preferences.MarshalSSZTo(dst); err != nil { + return + } + + // Offset (1) 'Auth' + dst = ssz.WriteOffset(dst, offset) + if b.Auth == nil { + b.Auth = new(SignedRequestAuthV1) + } + offset += b.Auth.SizeSSZ() + + // Field (1) 'Auth' + if dst, err = b.Auth.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the BuilderPreferencesRequestV1 object +func (b *BuilderPreferencesRequestV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 12 { + return ssz.ErrSize + } + + tail := buf + var o1 uint64 + + // Field (0) 'Preferences' + if b.Preferences == nil { + b.Preferences = new(BuilderPreferencesV1) + } + if err = b.Preferences.UnmarshalSSZ(buf[0:8]); err != nil { + return err + } + + // Offset (1) 'Auth' + if o1 = ssz.ReadOffset(buf[8:12]); o1 > size { + return ssz.ErrOffset + } + + if o1 != 12 { + return ssz.ErrInvalidVariableOffset + } + + // Field (1) 'Auth' + { + buf = tail[o1:] + if b.Auth == nil { + b.Auth = new(SignedRequestAuthV1) + } + if err = b.Auth.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the BuilderPreferencesRequestV1 object +func (b *BuilderPreferencesRequestV1) SizeSSZ() (size int) { + size = 12 + + // Field (1) 'Auth' + if b.Auth == nil { + b.Auth = new(SignedRequestAuthV1) + } + size += b.Auth.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the BuilderPreferencesRequestV1 object +func (b *BuilderPreferencesRequestV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(b) +} + +// HashTreeRootWith ssz hashes the BuilderPreferencesRequestV1 object with a hasher +func (b *BuilderPreferencesRequestV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Preferences' + if err = b.Preferences.HashTreeRootWith(hh); err != nil { + return + } + + // Field (1) 'Auth' + if err = b.Auth.HashTreeRootWith(hh); err != nil { + return + } + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the ProduceBuilderEntryV1 object to a target array +func (p *ProduceBuilderEntryV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(72) + + // Offset (0) 'Url' + dst = ssz.WriteOffset(dst, offset) + offset += len(p.Url) + + // Offset (1) 'Request' + dst = ssz.WriteOffset(dst, offset) + if p.Request == nil { + p.Request = new(BuilderPreferencesRequestV1) + } + offset += p.Request.SizeSSZ() + + // Field (2) 'MinBid' + dst = ssz.MarshalUint(dst, p.MinBid) + + // Field (3) 'BuilderBoostFactor' + dst = ssz.MarshalUint(dst, p.BuilderBoostFactor) + + // Field (4) 'Pubkey' + if size := len(p.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("--.Pubkey", size, 48) + return + } + dst = append(dst, p.Pubkey...) + + // Field (0) 'Url' + if size := len(p.Url); size > 2048 { + err = ssz.ErrBytesLengthFn("--.Url", size, 2048) + return + } + dst = append(dst, p.Url...) + + // Field (1) 'Request' + if dst, err = p.Request.MarshalSSZTo(dst); err != nil { + return + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 72 { + return ssz.ErrSize + } + + tail := buf + var o0, o1 uint64 + + // Offset (0) 'Url' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 72 { + return ssz.ErrInvalidVariableOffset + } + + // Offset (1) 'Request' + if o1 = ssz.ReadOffset(buf[4:8]); o1 > size || o0 > o1 { + return ssz.ErrOffset + } + + // Field (2) 'MinBid' + p.MinBid = ssz.UnmarshallUint[github_com_OffchainLabs_prysm_v7_consensus_types_primitives.Gwei](buf[8:16]) + + // Field (3) 'BuilderBoostFactor' + p.BuilderBoostFactor = ssz.UnmarshallUint[uint64](buf[16:24]) + + // Field (4) 'Pubkey' + if cap(p.Pubkey) == 0 { + p.Pubkey = make([]byte, 0, len(buf[24:72])) + } + p.Pubkey = append(p.Pubkey, buf[24:72]...) + + // Field (0) 'Url' + { + buf = tail[o0:o1] + if len(buf) > 2048 { + return ssz.ErrBytesLength + } + if cap(p.Url) == 0 { + p.Url = make([]byte, 0, len(buf)) + } + p.Url = append(p.Url, buf...) + } + + // Field (1) 'Request' + { + buf = tail[o1:] + if p.Request == nil { + p.Request = new(BuilderPreferencesRequestV1) + } + if err = p.Request.UnmarshalSSZ(buf); err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) SizeSSZ() (size int) { + size = 72 + + // Field (0) 'Url' + size += len(p.Url) + + // Field (1) 'Request' + if p.Request == nil { + p.Request = new(BuilderPreferencesRequestV1) + } + size += p.Request.SizeSSZ() + + return +} + +// HashTreeRoot ssz hashes the ProduceBuilderEntryV1 object +func (p *ProduceBuilderEntryV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the ProduceBuilderEntryV1 object with a hasher +func (p *ProduceBuilderEntryV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Url' + { + elemIndx := hh.Index() + byteLen := uint64(len(p.Url)) + if byteLen > 2048 { + err = ssz.ErrIncorrectListSize + return + } + hh.AppendBytes32(p.Url) + hh.MerkleizeWithMixin(elemIndx, byteLen, (2048+31)/32) + } + + // Field (1) 'Request' + if err = p.Request.HashTreeRootWith(hh); err != nil { + return + } + + // Field (2) 'MinBid' + ssz.PutUint(hh, p.MinBid) + + // Field (3) 'BuilderBoostFactor' + ssz.PutUint(hh, p.BuilderBoostFactor) + + // Field (4) 'Pubkey' + if size := len(p.Pubkey); size != 48 { + err = ssz.ErrBytesLengthFn("--.Pubkey", size, 48) + return + } + hh.PutBytes(p.Pubkey) + + hh.Merkleize(indx) + return +} + +// MarshalSSZ ssz marshals the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) MarshalSSZ() ([]byte, error) { + return ssz.MarshalSSZ(p) +} + +// MarshalSSZTo ssz marshals the ProduceBuilderEntryListV1 object to a target array +func (p *ProduceBuilderEntryListV1) MarshalSSZTo(buf []byte) (dst []byte, err error) { + dst = buf + offset := int(4) + + // Offset (0) 'Entries' + dst = ssz.WriteOffset(dst, offset) + for ii := 0; ii < len(p.Entries); ii++ { + offset += 4 + offset += p.Entries[ii].SizeSSZ() + } + + // Field (0) 'Entries' + if size := len(p.Entries); size > 64 { + err = ssz.ErrListTooBigFn("--.Entries", size, 64) + return + } + { + offset = 4 * len(p.Entries) + for ii := 0; ii < len(p.Entries); ii++ { + dst = ssz.WriteOffset(dst, offset) + offset += p.Entries[ii].SizeSSZ() + } + } + for ii := 0; ii < len(p.Entries); ii++ { + if dst, err = p.Entries[ii].MarshalSSZTo(dst); err != nil { + return + } + } + + return +} + +// UnmarshalSSZ ssz unmarshals the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) UnmarshalSSZ(buf []byte) error { + var err error + size := uint64(len(buf)) + if size < 4 { + return ssz.ErrSize + } + + tail := buf + var o0 uint64 + + // Offset (0) 'Entries' + if o0 = ssz.ReadOffset(buf[0:4]); o0 > size { + return ssz.ErrOffset + } + + if o0 != 4 { + return ssz.ErrInvalidVariableOffset + } + + // Field (0) 'Entries' + { + buf = tail[o0:] + num, err := ssz.DecodeDynamicLength(buf, 64) + if err != nil { + return err + } + p.Entries = make([]*ProduceBuilderEntryV1, num) + err = ssz.UnmarshalDynamic(buf, num, func(indx int, buf []byte) (err error) { + if p.Entries[indx] == nil { + p.Entries[indx] = new(ProduceBuilderEntryV1) + } + if err = p.Entries[indx].UnmarshalSSZ(buf); err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + } + return err +} + +// SizeSSZ returns the ssz encoded size in bytes for the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) SizeSSZ() (size int) { + size = 4 + + // Field (0) 'Entries' + for ii := 0; ii < len(p.Entries); ii++ { + size += 4 + size += p.Entries[ii].SizeSSZ() + } + + return +} + +// HashTreeRoot ssz hashes the ProduceBuilderEntryListV1 object +func (p *ProduceBuilderEntryListV1) HashTreeRoot() ([32]byte, error) { + return ssz.HashWithDefaultHasher(p) +} + +// HashTreeRootWith ssz hashes the ProduceBuilderEntryListV1 object with a hasher +func (p *ProduceBuilderEntryListV1) HashTreeRootWith(hh *ssz.Hasher) (err error) { + indx := hh.Index() + + // Field (0) 'Entries' + { + subIndx := hh.Index() + num := uint64(len(p.Entries)) + if num > 64 { + err = ssz.ErrIncorrectListSize + return + } + for _, elem := range p.Entries { + if err = elem.HashTreeRootWith(hh); err != nil { + return + } + } + hh.MerkleizeWithMixin(subIndx, num, 64) + } + + hh.Merkleize(indx) + return +} diff --git a/proto/prysm/v1alpha1/gloas_builder_api.proto b/proto/prysm/v1alpha1/gloas_builder_api.proto index 33780d323d79..47f829f98895 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.proto +++ b/proto/prysm/v1alpha1/gloas_builder_api.proto @@ -80,5 +80,5 @@ message ProduceBuilderEntryV1 { message ProduceBuilderEntryListV1 { repeated ProduceBuilderEntryV1 entries = 1 - [ (ethereum.eth.ext.ssz_max) = "256" ]; + [ (ethereum.eth.ext.ssz_max) = "64" ]; } diff --git a/proto/prysm/v1alpha1/gloas_builder_api.ssz.go b/proto/prysm/v1alpha1/gloas_builder_api.ssz.go index 21340d54f747..8c36fecfff49 100644 --- a/proto/prysm/v1alpha1/gloas_builder_api.ssz.go +++ b/proto/prysm/v1alpha1/gloas_builder_api.ssz.go @@ -1,3 +1,5 @@ +//go:build !minimal + // Code generated by fastssz. DO NOT EDIT. package eth From d65b72bd27a8f7e599fe3283ae789f13829ebba7 Mon Sep 17 00:00:00 2001 From: james-prysm Date: Tue, 28 Jul 2026 14:27:47 -0500 Subject: [PATCH 19/19] update based on 88's feedback --- validator/rpc/handlers_validator_config.go | 42 ++++++++++--------- .../rpc/handlers_validator_config_test.go | 16 +++---- validator/rpc/structs.go | 8 ++-- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/validator/rpc/handlers_validator_config.go b/validator/rpc/handlers_validator_config.go index ab09e1511ac4..175a72e9c22d 100644 --- a/validator/rpc/handlers_validator_config.go +++ b/validator/rpc/handlers_validator_config.go @@ -48,10 +48,10 @@ func (s *Server) GetBuilders(w http.ResponseWriter, r *http.Request) { } for _, e := range out.Builders { if e.MinBid == nil { - e.MinBid = out.DefaultMinBid + e.MinBid = out.MinBid } if e.BuilderBoostFactor == nil { - e.BuilderBoostFactor = out.DefaultBuilderBoostFactor + e.BuilderBoostFactor = out.BuilderBoostFactor } } httputil.WriteJson(w, out) @@ -174,9 +174,9 @@ func resolveBuilders(settings *proposer.Settings, key [fieldparams.BLSPubkeyLeng func builderConfigJSONFromConsensus(bc *proposer.BuilderConfig) *BuilderConfigJson { out := &BuilderConfigJson{ - Enabled: bc.Enabled, - DefaultMinBid: optUintStrPtr(bc.MinBid), - DefaultBuilderBoostFactor: optUintStrPtr(bc.BuilderBoostFactor), + Enabled: bc.Enabled, + MinBid: optUintStrPtr(bc.MinBid), + BuilderBoostFactor: optUintStrPtr(bc.BuilderBoostFactor), } if bc.Builders != nil { out.Builders = make([]*BuilderEntryJson, 0, len(bc.Builders)) @@ -207,15 +207,15 @@ func builderEntryJSONFromConsensus(be *proposer.BuilderEntry) *BuilderEntryJson func builderConfigFromJSON(in *BuilderConfigJson) (*proposer.BuilderConfig, error) { bc := &proposer.BuilderConfig{Enabled: in.Enabled} - if in.DefaultMinBid != nil { - v, err := parseUint(*in.DefaultMinBid, "default_min_bid") + if in.MinBid != nil { + v, err := parseUint(*in.MinBid, "min_bid") if err != nil { return nil, err } bc.MinBid = &v } - if in.DefaultBuilderBoostFactor != nil { - v, err := parseUint(*in.DefaultBuilderBoostFactor, "default_builder_boost_factor") + if in.BuilderBoostFactor != nil { + v, err := parseUint(*in.BuilderBoostFactor, "builder_boost_factor") if err != nil { return nil, err } @@ -228,24 +228,26 @@ func builderConfigFromJSON(in *BuilderConfigJson) (*proposer.BuilderConfig, erro return nil, errors.Errorf("builders exceeds %d entries", maxBuilderEntries) } // Non-nil (possibly empty) list means "use exactly these builders", not "inherit". + // Uniqueness is scoped by role: url entries by (url, auth_data), + // url-less p2p-policy entries by builder_pubkey. bc.Builders = make([]*proposer.BuilderEntry, 0, len(in.Builders)) - seen := make(map[string]bool, len(in.Builders)) - seenPubkey := make(map[string]bool, len(in.Builders)) + seenURL := make(map[string]bool, len(in.Builders)) + seenP2P := make(map[string]bool, len(in.Builders)) for i, entry := range in.Builders { be, err := builderEntryFromJSON(entry, i) if err != nil { return nil, err } - tuple := fmt.Sprintf("%s|%x|%x", be.URL, be.AuthData, be.Pubkey) - if seen[tuple] { - return nil, errors.Errorf("builders[%d]: duplicate url, auth_data and builder_pubkey", i) - } - seen[tuple] = true - if len(be.Pubkey) != 0 { - if seenPubkey[string(be.Pubkey)] { - return nil, errors.Errorf("builders[%d]: builder_pubkey appears on more than one entry", i) + if be.URL != "" { + key := fmt.Sprintf("%s|%x", be.URL, be.AuthData) + if seenURL[key] { + return nil, errors.Errorf("builders[%d]: two entries share the same url and auth_data", i) } - seenPubkey[string(be.Pubkey)] = true + seenURL[key] = true + } else if seenP2P[string(be.Pubkey)] { + return nil, errors.Errorf("builders[%d]: two p2p-policy entries share the same builder_pubkey", i) + } else { + seenP2P[string(be.Pubkey)] = true } bc.Builders = append(bc.Builders, be) } diff --git a/validator/rpc/handlers_validator_config_test.go b/validator/rpc/handlers_validator_config_test.go index fa5ff1f89535..6817272b7ac6 100644 --- a/validator/rpc/handlers_validator_config_test.go +++ b/validator/rpc/handlers_validator_config_test.go @@ -66,7 +66,7 @@ func deleteBuilders(t *testing.T, s *Server, pubkey string) *httptest.ResponseRe func TestServer_SetGetBuilders_RoundTrip(t *testing.T) { srv, keys := setupConfigServer(t, 1) pk := hexutil.Encode(keys[0][:]) - body := `{"enabled":true,"default_min_bid":"5","default_builder_boost_factor":"120",` + + body := `{"enabled":true,"min_bid":"5","builder_boost_factor":"120",` + `"builders":[{"url":"https://b.example","auth_data":"0x0102","max_execution_payment":"1000"}]}` w := postBuilders(t, srv, pk, body) @@ -75,8 +75,8 @@ func TestServer_SetGetBuilders_RoundTrip(t *testing.T) { _, cfg := getBuilders(t, srv, pk) require.NotNil(t, cfg.Enabled) require.Equal(t, true, *cfg.Enabled) - require.Equal(t, "5", *cfg.DefaultMinBid) - require.Equal(t, "120", *cfg.DefaultBuilderBoostFactor) + require.Equal(t, "5", *cfg.MinBid) + require.Equal(t, "120", *cfg.BuilderBoostFactor) require.Equal(t, 1, len(cfg.Builders)) require.Equal(t, "https://b.example", *cfg.Builders[0].Url) require.Equal(t, "1000", *cfg.Builders[0].MaxExecutionPayment) @@ -130,11 +130,11 @@ func TestServer_SetBuilders_Rejects(t *testing.T) { cases := map[string]struct { body, contains string }{ - "missing enabled": {`{"builders":[{"url":"https://a"}]}`, "enabled is required"}, - "entry has neither": {`{"enabled":true,"builders":[{"min_bid":"1"}]}`, "at least one of url and builder_pubkey"}, - "duplicate tuple": {`{"enabled":true,"builders":[{"url":"https://a"},{"url":"https://a"}]}`, "duplicate url, auth_data and builder_pubkey"}, - "builder_pubkey repeated": {`{"enabled":true,"builders":[{"builder_pubkey":"` + bpk + `"},{"url":"https://a","builder_pubkey":"` + bpk + `"}]}`, "builder_pubkey appears on more than one"}, - "invalid url": {`{"enabled":true,"builders":[{"url":"not a url"}]}`, "url is not a valid URL"}, + "missing enabled": {`{"builders":[{"url":"https://a"}]}`, "enabled is required"}, + "entry has neither": {`{"enabled":true,"builders":[{"min_bid":"1"}]}`, "at least one of url and builder_pubkey"}, + "same url and auth_data": {`{"enabled":true,"builders":[{"url":"https://a"},{"url":"https://a"}]}`, "share the same url and auth_data"}, + "p2p pubkey repeated": {`{"enabled":true,"builders":[{"builder_pubkey":"` + bpk + `"},{"builder_pubkey":"` + bpk + `"}]}`, "two p2p-policy entries share the same builder_pubkey"}, + "invalid url": {`{"enabled":true,"builders":[{"url":"not a url"}]}`, "url is not a valid URL"}, } for name, tc := range cases { t.Run(name, func(t *testing.T) { diff --git a/validator/rpc/structs.go b/validator/rpc/structs.go index 32bd52852c15..a4f9e100ff33 100644 --- a/validator/rpc/structs.go +++ b/validator/rpc/structs.go @@ -117,10 +117,10 @@ type GraffitiData struct { // builders") must serialize as [] rather than be dropped. On POST input an absent // field still decodes to nil, which means "inherit the validator client's builders". type BuilderConfigJson struct { - Enabled *bool `json:"enabled"` - Builders []*BuilderEntryJson `json:"builders"` - DefaultMinBid *string `json:"default_min_bid,omitempty"` - DefaultBuilderBoostFactor *string `json:"default_builder_boost_factor,omitempty"` + Enabled *bool `json:"enabled"` + Builders []*BuilderEntryJson `json:"builders"` + MinBid *string `json:"min_bid,omitempty"` + BuilderBoostFactor *string `json:"builder_boost_factor,omitempty"` } type BuilderEntryJson struct {