Skip to content

Commit

Permalink
Add flag to boost local block value (#12227)
Browse files Browse the repository at this point in the history
* Add builder bid fraction to compare with local block value and use builder bid if (bid * fraction) > local block value

* Prioritize local block construction over relay/builder block construction using a boost value for local block construction

* Refactor builder and local block value calculation to use percentage comparison

* Add a test for local with boost

* Use uint64

* Fix log
  • Loading branch information
terencechain authored Apr 4, 2023
1 parent 2618a11 commit 35e3eed
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 9 deletions.
7 changes: 7 additions & 0 deletions beacon-chain/node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,13 @@ func configureBuilderCircuitBreaker(cliCtx *cli.Context) error {
return err
}
}
if cliCtx.IsSet(flags.LocalBlockValueBoost.Name) {
c := params.BeaconConfig().Copy()
c.LocalBlockValueBoost = cliCtx.Uint64(flags.LocalBlockValueBoost.Name)
if err := params.SetActive(c); err != nil {
return err
}
}
return nil
}

Expand Down
25 changes: 18 additions & 7 deletions beacon-chain/rpc/prysm/v1alpha1/validator/proposer_bellatrix.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,32 +60,43 @@ func (vs *Server) setExecutionData(ctx context.Context, blk interfaces.SignedBea
return errors.Wrap(err, "failed to get execution payload")
}
// Compare payload values between local and builder. Default to the local value if it is higher.
localValue, err := localPayload.Value()
v, err := localPayload.Value()
if err != nil {
return errors.Wrap(err, "failed to get local payload value")
}
builderValue, err := builderPayload.Value()
localValue := v.Uint64()
v, err = builderPayload.Value()
if err != nil {
log.WithError(err).Warn("Proposer: failed to get builder payload value") // Default to local if can't get builder value.
}
builderValue := v.Uint64()

withdrawalsMatched, err := matchingWithdrawalsRoot(localPayload, builderPayload)
if err != nil {
return errors.Wrap(err, "failed to match withdrawals root")
}

// Use builder payload if the following in true:
// builder_bid_value * 100 > local_block_value * (local-block-value-boost + 100)
boost := params.BeaconConfig().LocalBlockValueBoost
higherValueBuilder := builderValue*100 > localValue*(100+boost)

// If we can't get the builder value, just use local block.
if builderValue.Cmp(localValue) > 0 && withdrawalsMatched { // Builder value is higher and withdrawals match.
if higherValueBuilder && withdrawalsMatched { // Builder value is higher and withdrawals match.
blk.SetBlinded(true)
if err := blk.SetExecution(builderPayload); err != nil {
log.WithError(err).Warn("Proposer: failed to set builder payload")
} else {
return nil
}
}
log.WithFields(logrus.Fields{
"localValue": localValue,
"builderValue": builderValue,
}).Warn("Proposer: using local execution payload because higher value")
if !higherValueBuilder {
log.WithFields(logrus.Fields{
"localGweiValue": localValue,
"localBoostPercentage": 100 + boost,
"builderGweiValue": builderValue,
}).Warn("Proposer: using local execution payload because higher value")
}
return blk.SetExecution(localPayload)
default: // Bellatrix case.
blk.SetBlinded(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ import (
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
"github.com/prysmaticlabs/prysm/v4/time/slots"
logTest "github.com/sirupsen/logrus/hooks/test"
)

func TestServer_setExecutionData(t *testing.T) {
hook := logTest.NewGlobal()

ctx := context.Background()
cfg := params.BeaconConfig().Copy()
cfg.BellatrixForkEpoch = 0
Expand Down Expand Up @@ -138,6 +141,7 @@ func TestServer_setExecutionData(t *testing.T) {
require.NoError(t, err)
wr, err := ssz.WithdrawalSliceRoot(withdrawals, fieldparams.MaxWithdrawalsPerPayload)
require.NoError(t, err)
builderValue := bytesutil.ReverseByteOrder(big.NewInt(1e9).Bytes())
bid := &ethpb.BuilderBidCapella{
Header: &v1.ExecutionPayloadHeaderCapella{
FeeRecipient: make([]byte, fieldparams.FeeRecipientLength),
Expand All @@ -154,7 +158,7 @@ func TestServer_setExecutionData(t *testing.T) {
WithdrawalsRoot: wr[:],
},
Pubkey: sk.PublicKey().Marshal(),
Value: bytesutil.PadTo([]byte{1}, 32),
Value: bytesutil.PadTo(builderValue, 32),
}
d := params.BeaconConfig().DomainApplicationBuilder
domain, err := signing.ComputeDomain(d, nil, nil)
Expand Down Expand Up @@ -183,11 +187,28 @@ func TestServer_setExecutionData(t *testing.T) {
t.Run("Builder configured. Local block has higher value", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: big.NewInt(3)}
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: big.NewInt(2 * 1e9)}
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block

require.LogsContain(t, hook, "builderGweiValue=1000000000 localBoostPercentage=100 localGweiValue=2000000000")
})
t.Run("Builder configured. Local block and boost has higher value", func(t *testing.T) {
cfg := params.BeaconConfig().Copy()
cfg.LocalBlockValueBoost = 1 // Boost 1%.
params.OverrideBeaconConfig(cfg)

blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
require.NoError(t, err)
vs.ExecutionEngineCaller = &powtesting.EngineClient{PayloadIDBytes: id, ExecutionPayloadCapella: &v1.ExecutionPayloadCapella{BlockNumber: 3}, BlockValue: big.NewInt(1e9)}
require.NoError(t, vs.setExecutionData(context.Background(), blk, capellaTransitionState))
e, err := blk.Block().Body().Execution()
require.NoError(t, err)
require.Equal(t, uint64(3), e.BlockNumber()) // Local block

require.LogsContain(t, hook, "builderGweiValue=1000000000 localBoostPercentage=101 localGweiValue=1000000000")
})
t.Run("Builder configured. Builder returns fault. Use local block", func(t *testing.T) {
blk, err := blocks.NewSignedBeaconBlock(util.NewBeaconBlockCapella())
Expand Down
5 changes: 5 additions & 0 deletions cmd/beacon-chain/flags/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ var (
Usage: "Number of total skip slot to fallback from using relay/builder to local execution engine for block construction in last epoch rolling window",
Value: 8,
}
LocalBlockValueBoost = &cli.Float64Flag{
Name: "local-block-value-boost",
Usage: "A percentage boost for local block construction. This is used to prioritize local block construction over relay/builder block construction" +
"Boost is an additional percentage to multiple local block value. Use builder block if: builder_bid_value * 100 > local_block_value * (local-block-value-boost + 100)",
}
// ExecutionEngineEndpoint provides an HTTP access endpoint to connect to an execution client on the execution layer
ExecutionEngineEndpoint = &cli.StringFlag{
Name: "execution-endpoint",
Expand Down
1 change: 1 addition & 0 deletions config/params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ type BeaconChainConfig struct {
// Mev-boost circuit breaker
MaxBuilderConsecutiveMissedSlots primitives.Slot // MaxBuilderConsecutiveMissedSlots defines the number of consecutive skip slot to fallback from using relay/builder to local execution engine for block construction.
MaxBuilderEpochMissedSlots primitives.Slot // MaxBuilderEpochMissedSlots is defines the number of total skip slot (per epoch rolling windows) to fallback from using relay/builder to local execution engine for block construction.
LocalBlockValueBoost uint64 // LocalBlockValueBoost is the value boost for local block construction. This is used to prioritize local block construction over relay/builder block construction.

// Execution engine timeout value
ExecutionEngineTimeoutValue uint64 // ExecutionEngineTimeoutValue defines the seconds to wait before timing out engine endpoints with execution payload execution semantics (newPayload, forkchoiceUpdated).
Expand Down

0 comments on commit 35e3eed

Please sign in to comment.