Skip to content

Commit

Permalink
[MEV boost] improve blinded block validator options and flow handling (
Browse files Browse the repository at this point in the history
…#5341)

* improve blinded block validator options

* automatically enable blinded-blocks when mev-boost is enabled
  • Loading branch information
tbenr authored Apr 14, 2022
1 parent 4116197 commit 9e8fe0d
Show file tree
Hide file tree
Showing 15 changed files with 117 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,20 @@ public void shouldSignBlock() {
assertThat(result).isCompletedWithValue(expectedSignature);
}

@Test
public void shouldSignBlindedBlock() {
final BeaconBlock block = dataStructureUtil.randomBlindedBeaconBlock(10);
final BLSSignature expectedSignature =
BLSSignature.fromBytesCompressed(
Bytes.fromBase64String(
"pbSSuf7h70JkzI/U157flTWPZDuaBXgRLj1HLMoCwjA4Xd0hMdGewn7G2HLZiQcNC9G6FSd1+0BT5PwknYez4ya6TccwpaGnsvWYLPf3SNIX5Ug7Yi1CF1fvEr3x9sZ0"));

final SafeFuture<BLSSignature> result = signer.signBlock(block, fork);
asyncRunner.executeQueuedActions();

assertThat(result).isCompletedWithValue(expectedSignature);
}

@Test
public void shouldCreateRandaoReveal() {
final BLSSignature expectedSignature =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,10 @@ public BeaconBlock randomBeaconBlock(UInt64 slotNum) {
body);
}

public BeaconBlock randomBlindedBeaconBlock(long slotNum) {
return randomBlindedBeaconBlock(UInt64.valueOf(slotNum));
}

public BeaconBlock randomBlindedBeaconBlock(UInt64 slotNum) {
final UInt64 proposerIndex = randomUInt64();
Bytes32 previousRoot = randomBytes32();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

package tech.pegasys.teku.cli.options;

import static tech.pegasys.teku.validator.api.ValidatorConfig.DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;

import java.nio.file.Path;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
Expand Down Expand Up @@ -102,16 +100,6 @@ public class ValidatorOptions {
arity = "0..1")
private boolean generateEarlyAttestations = ValidatorConfig.DEFAULT_GENERATE_EARLY_ATTESTATIONS;

@Option(
names = {"--Xvalidators-blinded-blocks-api-enabled"},
paramLabel = "<BOOLEAN>",
showDefaultValue = Visibility.ALWAYS,
description = "Use blinded block api calls",
fallbackValue = "true",
hidden = true,
arity = "0..1")
private boolean blindedBlocksApiEnabled = DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;

public void configure(TekuConfiguration.Builder builder) {
if (validatorPerformanceTrackingEnabled != null) {
if (validatorPerformanceTrackingEnabled) {
Expand All @@ -125,7 +113,6 @@ public void configure(TekuConfiguration.Builder builder) {
config ->
config
.validatorKeystoreLockingEnabled(validatorKeystoreLockingEnabled)
.blindedBeaconBlocksApiEnabled(blindedBlocksApiEnabled)
.validatorPerformanceTrackingMode(validatorPerformanceTrackingMode)
.validatorExternalSignerSlashingProtectionEnabled(
validatorExternalSignerSlashingProtectionEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

package tech.pegasys.teku.cli.options;

import static tech.pegasys.teku.validator.api.ValidatorConfig.DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;

import picocli.CommandLine.Help.Visibility;
import picocli.CommandLine.Option;
import tech.pegasys.teku.config.TekuConfiguration;
Expand Down Expand Up @@ -56,13 +58,24 @@ public class ValidatorProposerOptions {
private boolean proposerMevBoostEnabled =
ValidatorConfig.DEFAULT_VALIDATOR_PROPOSER_MEV_BOOST_ENABLED;

@Option(
names = {"--Xvalidators-proposer-blinded-blocks-enabled"},
paramLabel = "<BOOLEAN>",
showDefaultValue = Visibility.ALWAYS,
description = "Use blinded blocks when in block production duties",
fallbackValue = "true",
hidden = true,
arity = "0..1")
private boolean blindedBlocksEnabled = DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;

public void configure(TekuConfiguration.Builder builder) {
builder.validator(
config ->
config
.proposerDefaultFeeRecipient(proposerDefaultFeeRecipient)
.proposerConfigSource(proposerConfig)
.refreshProposerConfigFromSource(proposerConfigRefreshEnabled)
.proposerMevBoostEnabled(proposerMevBoostEnabled));
.proposerMevBoostEnabled(proposerMevBoostEnabled)
.blindedBeaconBlocksEnabled(blindedBlocksEnabled));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

package tech.pegasys.teku.cli.subcommand;

import static tech.pegasys.teku.validator.api.ValidatorConfig.DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;

import java.net.URI;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -91,7 +89,6 @@ static OkHttpValidatorRestApiClient createApiClient(final URI baseEndpoint) {
// Strip any authentication info from the URL to ensure it doesn't get logged.
apiEndpoint = apiEndpoint.newBuilder().username("").password("").build();
final OkHttpClient okHttpClient = httpClientBuilder.build();
return new OkHttpValidatorRestApiClient(
apiEndpoint, okHttpClient, DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED);
return new OkHttpValidatorRestApiClient(apiEndpoint, okHttpClient);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,33 @@ public void shouldReportAddressIfFeeRecipientSpecified() {

@Test
public void shouldEnableBlindedBeaconBlocks() {
final String[] args = {"--Xvalidators-blinded-blocks-api-enabled", "true"};
final String[] args = {"--Xvalidators-proposer-blinded-blocks-enabled", "true"};
final TekuConfiguration config = getTekuConfigurationFromArguments(args);
assertThat(config.validatorClient().getValidatorConfig().isBlindedBeaconBlocksApiEnabled())
assertThat(config.validatorClient().getValidatorConfig().isBlindedBeaconBlocksEnabled())
.isTrue();
}

@Test
public void shouldNotUseBlindedBeaconBlocksByDefault() {
final String[] args = {};
final TekuConfiguration config = getTekuConfigurationFromArguments(args);
assertThat(config.validatorClient().getValidatorConfig().isBlindedBeaconBlocksApiEnabled())
assertThat(config.validatorClient().getValidatorConfig().isBlindedBeaconBlocksEnabled())
.isFalse();
}

@Test
public void shouldEnableMevBoostWithBlindedBlocks() {
final String[] args = {"--Xvalidators-proposer-mev-boost-enabled", "true"};
final TekuConfiguration config = getTekuConfigurationFromArguments(args);
assertThat(config.validatorClient().getValidatorConfig().isProposerMevBoostEnabled()).isTrue();
assertThat(config.validatorClient().getValidatorConfig().isBlindedBeaconBlocksEnabled())
.isTrue();
}

@Test
public void shouldNotUseMevBoostByDefault() {
final String[] args = {};
final TekuConfiguration config = getTekuConfigurationFromArguments(args);
assertThat(config.validatorClient().getValidatorConfig().isProposerMevBoostEnabled()).isFalse();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@
import java.util.List;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException;
import tech.pegasys.teku.spec.datastructures.eth1.Eth1Address;

public class ValidatorConfig {
private static final Logger LOG = LogManager.getLogger();

private static final int DEFAULT_REST_API_PORT = 5051;
public static final String DEFAULT_BEACON_NODE_API_ENDPOINT =
Expand Down Expand Up @@ -60,7 +63,7 @@ public class ValidatorConfig {
private final Optional<Eth1Address> proposerDefaultFeeRecipient;
private final Optional<String> proposerConfigSource;
private final boolean refreshProposerConfigFromSource;
private final boolean blindedBeaconBlocksApiEnabled;
private final boolean blindedBeaconBlocksEnabled;
private final boolean proposerMevBoostEnabled;

private ValidatorConfig(
Expand All @@ -83,7 +86,7 @@ private ValidatorConfig(
final Optional<String> proposerConfigSource,
final boolean refreshProposerConfigFromSource,
final boolean proposerMevBoostEnabled,
final boolean blindedBeaconBlocksApiEnabled) {
final boolean blindedBeaconBlocksEnabled) {
this.validatorKeys = validatorKeys;
this.validatorExternalSignerPublicKeySources = validatorExternalSignerPublicKeySources;
this.validatorExternalSignerUrl = validatorExternalSignerUrl;
Expand All @@ -105,7 +108,7 @@ private ValidatorConfig(
this.proposerDefaultFeeRecipient = proposerDefaultFeeRecipient;
this.proposerConfigSource = proposerConfigSource;
this.refreshProposerConfigFromSource = refreshProposerConfigFromSource;
this.blindedBeaconBlocksApiEnabled = blindedBeaconBlocksApiEnabled;
this.blindedBeaconBlocksEnabled = blindedBeaconBlocksEnabled;
this.proposerMevBoostEnabled = proposerMevBoostEnabled;
}

Expand Down Expand Up @@ -180,8 +183,8 @@ public boolean getRefreshProposerConfigFromSource() {
return refreshProposerConfigFromSource;
}

public boolean isBlindedBeaconBlocksApiEnabled() {
return blindedBeaconBlocksApiEnabled;
public boolean isBlindedBeaconBlocksEnabled() {
return blindedBeaconBlocksEnabled;
}

public boolean isProposerMevBoostEnabled() {
Expand Down Expand Up @@ -222,7 +225,7 @@ public static final class Builder {
private boolean refreshProposerConfigFromSource =
DEFAULT_VALIDATOR_PROPOSER_CONFIG_REFRESH_ENABLED;
private boolean proposerMevBoostEnabled = DEFAULT_VALIDATOR_PROPOSER_MEV_BOOST_ENABLED;
private boolean blindedBlocksApiEnabled = DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;
private boolean blindedBlocksEnabled = DEFAULT_VALIDATOR_BLINDED_BLOCKS_ENABLED;

private Builder() {}

Expand Down Expand Up @@ -353,8 +356,8 @@ public Builder proposerMevBoostEnabled(final boolean proposerMevBoostEnabled) {
return this;
}

public Builder blindedBeaconBlocksApiEnabled(final boolean blindedBeaconBlockEnabled) {
this.blindedBlocksApiEnabled = blindedBeaconBlockEnabled;
public Builder blindedBeaconBlocksEnabled(final boolean blindedBeaconBlockEnabled) {
this.blindedBlocksEnabled = blindedBeaconBlockEnabled;
return this;
}

Expand All @@ -363,6 +366,7 @@ public ValidatorConfig build() {
validateExternalSignerKeystoreAndPasswordFileConfig();
validateExternalSignerTruststoreAndPasswordFileConfig();
validateExternalSignerURLScheme();
validateMevBoostAndBlindedBlocks();
return new ValidatorConfig(
validatorKeys,
validatorExternalSignerPublicKeySources,
Expand All @@ -383,7 +387,7 @@ public ValidatorConfig build() {
proposerConfigSource,
refreshProposerConfigFromSource,
proposerMevBoostEnabled,
blindedBlocksApiEnabled);
blindedBlocksEnabled);
}

private void validateExternalSignerUrlAndPublicKeys() {
Expand Down Expand Up @@ -432,6 +436,14 @@ private void validateExternalSignerURLScheme() {
}
}

private void validateMevBoostAndBlindedBlocks() {
if (proposerMevBoostEnabled && !blindedBlocksEnabled) {
LOG.info(
"'--Xvalidators-proposer-mev-boost-enabled' requires '--Xvalidators-proposer-blinded-blocks-enabled', enabling it");
blindedBlocksEnabled = true;
}
}

private static boolean isURLSchemeHttps(final URL url) {
final String protocol = url.getProtocol();
return protocol != null && protocol.equalsIgnoreCase("https");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ public static ValidatorClientService create(
final AsyncRunner asyncRunner = services.createAsyncRunner("validator");
final boolean generateEarlyAttestations =
config.getValidatorConfig().generateEarlyAttestations();
final boolean blindedBlocksEnabled =
config.getValidatorConfig().isBlindedBeaconBlocksApiEnabled();
final BeaconNodeApi beaconNodeApi =
config
.getValidatorConfig()
Expand All @@ -112,8 +110,7 @@ public static ValidatorClientService create(
asyncRunner,
endpoint,
config.getSpec(),
generateEarlyAttestations,
blindedBlocksEnabled))
generateEarlyAttestations))
.orElseGet(
() ->
InProcessBeaconNodeApi.create(
Expand Down Expand Up @@ -194,7 +191,11 @@ private void initializeValidators(
this.validatorIndexProvider =
new ValidatorIndexProvider(validators, validatorApiChannel, asyncRunner);
final BlockDutyFactory blockDutyFactory =
new BlockDutyFactory(forkProvider, validatorApiChannel, spec);
new BlockDutyFactory(
forkProvider,
validatorApiChannel,
config.getValidatorConfig().isBlindedBeaconBlocksEnabled(),
spec);
final AttestationDutyFactory attestationDutyFactory =
new AttestationDutyFactory(spec, forkProvider, validatorApiChannel);
final BeaconCommitteeSubscriptions beaconCommitteeSubscriptions =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,23 @@ public class BlockDutyFactory implements DutyFactory<BlockProductionDuty, Duty>
private final ForkProvider forkProvider;
private final ValidatorApiChannel validatorApiChannel;
private final Spec spec;
private final boolean useBlindedBlock;

public BlockDutyFactory(
final ForkProvider forkProvider,
final ValidatorApiChannel validatorApiChannel,
final boolean useBlindedBlock,
final Spec spec) {
this.forkProvider = forkProvider;

this.useBlindedBlock = useBlindedBlock;
this.validatorApiChannel = validatorApiChannel;
this.spec = spec;
}

@Override
public BlockProductionDuty createProductionDuty(final UInt64 slot, final Validator validator) {
return new BlockProductionDuty(validator, slot, forkProvider, validatorApiChannel, spec);
return new BlockProductionDuty(
validator, slot, forkProvider, validatorApiChannel, useBlindedBlock, spec);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,21 @@ public class BlockProductionDuty implements Duty {
private final UInt64 slot;
private final ForkProvider forkProvider;
private final ValidatorApiChannel validatorApiChannel;
private final boolean useBlindedBlock;
private final Spec spec;

public BlockProductionDuty(
final Validator validator,
final UInt64 slot,
final ForkProvider forkProvider,
final ValidatorApiChannel validatorApiChannel,
final boolean useBlindedBlock,
final Spec spec) {
this.validator = validator;
this.slot = slot;
this.forkProvider = forkProvider;
this.validatorApiChannel = validatorApiChannel;
this.useBlindedBlock = useBlindedBlock;
this.spec = spec;
}

Expand Down Expand Up @@ -82,7 +85,7 @@ private SafeFuture<DutyResult> sendBlock(final SignedBeaconBlock signedBlock) {

public SafeFuture<Optional<BeaconBlock>> createUnsignedBlock(final BLSSignature randaoReveal) {
return validatorApiChannel.createUnsignedBlock(
slot, randaoReveal, validator.getGraffiti(), false);
slot, randaoReveal, validator.getGraffiti(), useBlindedBlock);
}

public SafeFuture<BLSSignature> createRandaoReveal(final ForkInfo forkInfo) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import tech.pegasys.teku.bls.BLSSignature;
import tech.pegasys.teku.core.signatures.Signer;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
Expand Down Expand Up @@ -62,22 +64,34 @@ class BlockProductionDutyTest {
private final ForkInfo fork = dataStructureUtil.randomForkInfo();
private final ValidatorLogger validatorLogger = mock(ValidatorLogger.class);

private final BlockProductionDuty duty =
new BlockProductionDuty(validator, SLOT, forkProvider, validatorApiChannel, spec);
private BlockProductionDuty duty;

@BeforeEach
public void setUp() {
duty = new BlockProductionDuty(validator, SLOT, forkProvider, validatorApiChannel, false, spec);
when(forkProvider.getForkInfo(any())).thenReturn(completedFuture(fork));
}

@Test
public void shouldCreateAndPublishBlock() {
@ParameterizedTest
@ValueSource(booleans = {true, false})
public void shouldCreateAndPublishBlock(final boolean isBlindedBlocksEnabled) {
duty =
new BlockProductionDuty(
validator, SLOT, forkProvider, validatorApiChannel, isBlindedBlocksEnabled, spec);
final BLSSignature randaoReveal = dataStructureUtil.randomSignature();
final BLSSignature blockSignature = dataStructureUtil.randomSignature();
final BeaconBlock unsignedBlock = dataStructureUtil.randomBeaconBlock(SLOT.longValue());
final BeaconBlock unsignedBlock;

if (isBlindedBlocksEnabled) {
unsignedBlock = dataStructureUtil.randomBlindedBeaconBlock(SLOT);
} else {
unsignedBlock = dataStructureUtil.randomBeaconBlock(SLOT);
}

when(signer.createRandaoReveal(spec.computeEpochAtSlot(SLOT), fork))
.thenReturn(completedFuture(randaoReveal));
when(validatorApiChannel.createUnsignedBlock(SLOT, randaoReveal, Optional.of(graffiti), false))
when(validatorApiChannel.createUnsignedBlock(
SLOT, randaoReveal, Optional.of(graffiti), isBlindedBlocksEnabled))
.thenReturn(completedFuture(Optional.of(unsignedBlock)));
when(signer.signBlock(unsignedBlock, fork)).thenReturn(completedFuture(blockSignature));
final SignedBeaconBlock signedBlock =
Expand Down
Loading

0 comments on commit 9e8fe0d

Please sign in to comment.