mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Beacon api: produce block should skip mev-boost (#11488)
* Beacon api: produce block should skip mev-boost * Update comments * Additional test and comments * Update beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com> * Fix suggestion Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
This commit is contained in:
@@ -270,6 +270,12 @@ func (vs *Server) GetSyncCommitteeDuties(ctx context.Context, req *ethpbv2.SyncC
|
||||
}
|
||||
|
||||
// ProduceBlockV2 requests the beacon node to produce a valid unsigned beacon block, which can then be signed by a proposer and submitted.
|
||||
// By definition `/eth/v2/validator/blocks/{slot}`, does not produce block using mev-boost and relayer network.
|
||||
// The following endpoint states that the returned object is a BeaconBlock, not a BlindedBeaconBlock. As such, the block must return a full ExecutionPayload:
|
||||
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.3.0#/Validator/produceBlockV2
|
||||
//
|
||||
// To use mev-boost and relayer network. It's recommended to use the following endpoint:
|
||||
// https://github.com/ethereum/beacon-APIs/blob/master/apis/validator/blinded_block.yaml
|
||||
func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockRequest) (*ethpbv2.ProduceBlockResponseV2, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.ProduceBlockV2")
|
||||
defer span.End()
|
||||
@@ -283,6 +289,7 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
|
||||
Slot: req.Slot,
|
||||
RandaoReveal: req.RandaoReveal,
|
||||
Graffiti: req.Graffiti,
|
||||
SkipMevBoost: true, // Skip mev-boost and relayer network
|
||||
}
|
||||
v1alpha1resp, err := vs.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
|
||||
if err != nil {
|
||||
@@ -334,6 +341,12 @@ func (vs *Server) ProduceBlockV2(ctx context.Context, req *ethpbv1.ProduceBlockR
|
||||
// ProduceBlockV2SSZ requests the beacon node to produce a valid unsigned beacon block, which can then be signed by a proposer and submitted.
|
||||
//
|
||||
// The produced block is in SSZ form.
|
||||
// By definition `/eth/v2/validator/blocks/{slot}/ssz`, does not produce block using mev-boost and relayer network:
|
||||
// The following endpoint states that the returned object is a BeaconBlock, not a BlindedBeaconBlock. As such, the block must return a full ExecutionPayload:
|
||||
// https://ethereum.github.io/beacon-APIs/?urls.primaryName=v2.3.0#/Validator/produceBlockV2
|
||||
//
|
||||
// To use mev-boost and relayer network. It's recommended to use the following endpoint:
|
||||
// https://github.com/ethereum/beacon-APIs/blob/master/apis/validator/blinded_block.yaml
|
||||
func (vs *Server) ProduceBlockV2SSZ(ctx context.Context, req *ethpbv1.ProduceBlockRequest) (*ethpbv2.SSZContainer, error) {
|
||||
ctx, span := trace.StartSpan(ctx, "validator.ProduceBlockV2SSZ")
|
||||
defer span.End()
|
||||
@@ -347,6 +360,7 @@ func (vs *Server) ProduceBlockV2SSZ(ctx context.Context, req *ethpbv1.ProduceBlo
|
||||
Slot: req.Slot,
|
||||
RandaoReveal: req.RandaoReveal,
|
||||
Graffiti: req.Graffiti,
|
||||
SkipMevBoost: true, // Skip mev-boost and relayer network
|
||||
}
|
||||
v1alpha1resp, err := vs.V1Alpha1Server.GetBeaconBlock(ctx, v1alpha1req)
|
||||
if err != nil {
|
||||
|
||||
@@ -935,6 +935,9 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
},
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, params.BeaconConfig().MaxProposerSlashings)
|
||||
@@ -1004,6 +1007,9 @@ func TestProduceBlockV2(t *testing.T) {
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
v1Server.V1Alpha1Server.BeaconDB = db
|
||||
require.NoError(t, v1Alpha1Server.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []types.ValidatorIndex{348},
|
||||
[]*ethpbalpha.ValidatorRegistrationV1{{FeeRecipient: bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength), Pubkey: bytesutil.PadTo([]byte{}, fieldparams.BLSPubkeyLength)}}))
|
||||
|
||||
resp, err := v1Server.ProduceBlockV2(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
@@ -1424,6 +1430,9 @@ func TestProduceBlockV2SSZ(t *testing.T) {
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{
|
||||
HasConfigured: true,
|
||||
},
|
||||
}
|
||||
|
||||
proposerSlashings := make([]*ethpbalpha.ProposerSlashing, 1)
|
||||
@@ -1490,6 +1499,9 @@ func TestProduceBlockV2SSZ(t *testing.T) {
|
||||
Graffiti: graffiti[:],
|
||||
}
|
||||
v1Server.V1Alpha1Server.BeaconDB = db
|
||||
require.NoError(t, v1Alpha1Server.BeaconDB.SaveRegistrationsByValidatorIDs(ctx, []types.ValidatorIndex{348},
|
||||
[]*ethpbalpha.ValidatorRegistrationV1{{FeeRecipient: bytesutil.PadTo([]byte{}, fieldparams.FeeRecipientLength), Pubkey: bytesutil.PadTo([]byte{}, fieldparams.BLSPubkeyLength)}}))
|
||||
|
||||
resp, err := v1Server.ProduceBlockV2SSZ(ctx, req)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, ethpbv2.Version_BELLATRIX, resp.Version)
|
||||
|
||||
@@ -44,22 +44,24 @@ func (vs *Server) getBellatrixBeaconBlock(ctx context.Context, req *ethpb.BlockR
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registered, err := vs.validatorRegistered(ctx, altairBlk.ProposerIndex)
|
||||
if registered && err == nil {
|
||||
builderReady, b, err := vs.GetAndBuildBlindBlock(ctx, altairBlk)
|
||||
if err != nil {
|
||||
// In the event of an error, the node should fall back to default execution engine for building block.
|
||||
log.WithError(err).Error("Failed to build a block from external builder, falling " +
|
||||
"back to local execution client")
|
||||
builderGetPayloadMissCount.Inc()
|
||||
} else if builderReady {
|
||||
return b, nil
|
||||
if !req.SkipMevBoost {
|
||||
registered, err := vs.validatorRegistered(ctx, altairBlk.ProposerIndex)
|
||||
if registered && err == nil {
|
||||
builderReady, b, err := vs.GetAndBuildBlindBlock(ctx, altairBlk)
|
||||
if err != nil {
|
||||
// In the event of an error, the node should fall back to default execution engine for building block.
|
||||
log.WithError(err).Error("Failed to build a block from external builder, falling " +
|
||||
"back to local execution client")
|
||||
builderGetPayloadMissCount.Inc()
|
||||
} else if builderReady {
|
||||
return b, nil
|
||||
}
|
||||
} else if err != nil {
|
||||
log.WithError(err).WithFields(logrus.Fields{
|
||||
"slot": req.Slot,
|
||||
"validatorIndex": altairBlk.ProposerIndex,
|
||||
}).Error("Could not determine validator has registered. Defaulting to local execution client")
|
||||
}
|
||||
} else if err != nil {
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": req.Slot,
|
||||
"validatorIndex": altairBlk.ProposerIndex,
|
||||
}).Errorf("Could not determine validator has registered. Default to local execution client: %v", err)
|
||||
}
|
||||
payload, err := vs.getExecutionPayload(ctx, req.Slot, altairBlk.ProposerIndex, bytesutil.ToBytes32(altairBlk.ParentRoot))
|
||||
if err != nil {
|
||||
|
||||
@@ -629,6 +629,111 @@ func TestServer_GetBellatrixBeaconBlock_HappyCase(t *testing.T) {
|
||||
require.DeepEqual(t, emptyPayload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
|
||||
}
|
||||
|
||||
func TestServer_GetBellatrixBeaconBlock_LocalProgressingWithBuilderSkipped(t *testing.T) {
|
||||
db := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
hook := logTest.NewGlobal()
|
||||
|
||||
terminalBlockHash := bytesutil.PadTo([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 32)
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.BellatrixForkEpoch = 2
|
||||
cfg.AltairForkEpoch = 1
|
||||
cfg.TerminalBlockHash = common.BytesToHash(terminalBlockHash)
|
||||
cfg.TerminalBlockHashActivationEpoch = 2
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
beaconState, privKeys := util.DeterministicGenesisState(t, 64)
|
||||
stateRoot, err := beaconState.HashTreeRoot(ctx)
|
||||
require.NoError(t, err, "Could not hash genesis state")
|
||||
|
||||
genesis := consensusblocks.NewGenesisBlock(stateRoot[:])
|
||||
wsb, err := blocks.NewSignedBeaconBlock(genesis)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, db.SaveBlock(ctx, wsb), "Could not save genesis block")
|
||||
|
||||
parentRoot, err := genesis.Block.HashTreeRoot()
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, parentRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
|
||||
|
||||
bellatrixSlot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
|
||||
require.NoError(t, err)
|
||||
|
||||
emptyPayload := &v1.ExecutionPayload{
|
||||
ParentHash: make([]byte, fieldparams.RootLength),
|
||||
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
|
||||
StateRoot: make([]byte, fieldparams.RootLength),
|
||||
ReceiptsRoot: make([]byte, fieldparams.RootLength),
|
||||
LogsBloom: make([]byte, fieldparams.LogsBloomLength),
|
||||
PrevRandao: make([]byte, fieldparams.RootLength),
|
||||
BaseFeePerGas: make([]byte, fieldparams.RootLength),
|
||||
BlockHash: make([]byte, fieldparams.RootLength),
|
||||
}
|
||||
var scBits [fieldparams.SyncAggregateSyncCommitteeBytesLength]byte
|
||||
blk := ðpb.SignedBeaconBlockBellatrix{
|
||||
Block: ðpb.BeaconBlockBellatrix{
|
||||
Slot: bellatrixSlot + 1,
|
||||
ParentRoot: parentRoot[:],
|
||||
StateRoot: genesis.Block.StateRoot,
|
||||
Body: ðpb.BeaconBlockBodyBellatrix{
|
||||
RandaoReveal: genesis.Block.Body.RandaoReveal,
|
||||
Graffiti: genesis.Block.Body.Graffiti,
|
||||
Eth1Data: genesis.Block.Body.Eth1Data,
|
||||
SyncAggregate: ðpb.SyncAggregate{SyncCommitteeBits: scBits[:], SyncCommitteeSignature: make([]byte, 96)},
|
||||
ExecutionPayload: emptyPayload,
|
||||
},
|
||||
},
|
||||
Signature: genesis.Signature,
|
||||
}
|
||||
|
||||
blkRoot, err := blk.Block.HashTreeRoot()
|
||||
require.NoError(t, err, "Could not get signing root")
|
||||
require.NoError(t, db.SaveState(ctx, beaconState, blkRoot), "Could not save genesis state")
|
||||
require.NoError(t, db.SaveHeadBlockRoot(ctx, blkRoot), "Could not save genesis state")
|
||||
|
||||
proposerServer := &Server{
|
||||
HeadFetcher: &blockchainTest.ChainService{State: beaconState, Root: parentRoot[:], Optimistic: false},
|
||||
TimeFetcher: &blockchainTest.ChainService{Genesis: time.Now()},
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
BlockReceiver: &blockchainTest.ChainService{},
|
||||
HeadUpdater: &blockchainTest.ChainService{},
|
||||
ChainStartFetcher: &mockExecution.Chain{},
|
||||
Eth1InfoFetcher: &mockExecution.Chain{},
|
||||
MockEth1Votes: true,
|
||||
AttPool: attestations.NewPool(),
|
||||
SlashingsPool: slashings.NewPool(),
|
||||
ExitPool: voluntaryexits.NewPool(),
|
||||
StateGen: stategen.New(db),
|
||||
SyncCommitteePool: synccommittee.NewStore(),
|
||||
ExecutionEngineCaller: &mockExecution.EngineClient{
|
||||
PayloadIDBytes: &v1.PayloadIDBytes{1},
|
||||
ExecutionPayload: emptyPayload,
|
||||
},
|
||||
BeaconDB: db,
|
||||
ProposerSlotIndexCache: cache.NewProposerPayloadIDsCache(),
|
||||
BlockBuilder: &builderTest.MockBuilderService{},
|
||||
}
|
||||
proposerServer.ProposerSlotIndexCache.SetProposerAndPayloadIDs(17, 11, [8]byte{'a'}, parentRoot)
|
||||
|
||||
randaoReveal, err := util.RandaoReveal(beaconState, 0, privKeys)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Configure builder, this should fail if it's not local engine processing
|
||||
proposerServer.BlockBuilder = &builderTest.MockBuilderService{HasConfigured: true, ErrGetHeader: errors.New("bad)")}
|
||||
block, err := proposerServer.getBellatrixBeaconBlock(ctx, ðpb.BlockRequest{
|
||||
Slot: bellatrixSlot + 1,
|
||||
RandaoReveal: randaoReveal,
|
||||
SkipMevBoost: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
bellatrixBlk, ok := block.GetBlock().(*ethpb.GenericBeaconBlock_Bellatrix)
|
||||
require.Equal(t, true, ok)
|
||||
require.LogsContain(t, hook, "Computed state root")
|
||||
require.DeepEqual(t, emptyPayload, bellatrixBlk.Bellatrix.Body.ExecutionPayload) // Payload should equal.
|
||||
}
|
||||
|
||||
func TestServer_GetBellatrixBeaconBlock_BuilderCase(t *testing.T) {
|
||||
db := dbTest.SetupDB(t)
|
||||
ctx := context.Background()
|
||||
|
||||
1166
proto/prysm/v1alpha1/validator.pb.go
generated
1166
proto/prysm/v1alpha1/validator.pb.go
generated
File diff suppressed because it is too large
Load Diff
@@ -522,6 +522,8 @@ message BlockRequest {
|
||||
// Validator's 32 byte graffiti message for the new block.
|
||||
bytes graffiti = 3 [(ethereum.eth.ext.ssz_size) = "32"];
|
||||
|
||||
// Signal server to skip outsourcing block request from mev-boost/relayer so that returned block will always be a local block.
|
||||
bool skip_mev_boost = 4;
|
||||
}
|
||||
|
||||
message ProposeResponse {
|
||||
|
||||
Reference in New Issue
Block a user