Skip to content

Commit

Permalink
[MEV Boost\Builder] Implement builder_status and health check
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov committed May 4, 2022
1 parent e2e4b3b commit 6968acb
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.execution.BuilderStatus;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader;
import tech.pegasys.teku.spec.datastructures.execution.PowBlock;
Expand Down Expand Up @@ -102,9 +103,9 @@ private ExecutionLayerChannelImpl(
final ExecutionEngineClient executionEngineClient,
final Optional<ExecutionBuilderClient> executionBuilderClient,
final Spec spec) {
this.spec = spec;
this.executionEngineClient = executionEngineClient;
this.executionBuilderClient = executionBuilderClient;
this.spec = spec;
}

private static <K> K unwrapResponseOrThrow(Response<K> response) {
Expand Down Expand Up @@ -212,6 +213,22 @@ public SafeFuture<TransitionConfiguration> engineExchangeTransitionConfiguration
remoteTransitionConfiguration));
}

@Override
public SafeFuture<BuilderStatus> builderStatus() {
if (executionBuilderClient.isEmpty()) {
LOG.trace("Skipping calling builderStatus() because execution builder is not enabled.");
return SafeFuture.completedFuture(null);
}
LOG.trace("calling builderStatus()");
return executionBuilderClient
.get()
.status()
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(___ -> BuilderStatus.withOkStatus())
.exceptionally(BuilderStatus::withFailedStatus)
.thenPeek(builderStatus -> LOG.trace("builderStatus() -> {}", builderStatus));
}

@Override
public SafeFuture<ExecutionPayloadHeader> getPayloadHeader(
final Bytes8 payloadId, final UInt64 slot) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2022 ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/

package tech.pegasys.teku.spec.datastructures.execution;

import com.google.common.base.MoreObjects;

public class BuilderStatus {

private final Status status;
private final String errorMessage;

private BuilderStatus(final Status status, final String errorMessage) {
this.status = status;
this.errorMessage = errorMessage;
}

public static BuilderStatus withOkStatus() {
return new BuilderStatus(Status.OK, null);
}

public static BuilderStatus withFailedStatus(final Throwable cause) {
return new BuilderStatus(null, cause.getMessage());
}

public Status getStatus() {
return status;
}

public String getErrorMessage() {
return errorMessage;
}

public boolean hasFailed() {
return status == null;
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("status", status)
.add("errorMessage", errorMessage)
.toString();
}

public enum Status {
OK
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import tech.pegasys.teku.infrastructure.events.ChannelInterface;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.execution.BuilderStatus;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader;
import tech.pegasys.teku.spec.datastructures.execution.PowBlock;
Expand Down Expand Up @@ -63,6 +64,11 @@ public SafeFuture<TransitionConfiguration> engineExchangeTransitionConfiguration
return SafeFuture.completedFuture(transitionConfiguration);
}

@Override
public SafeFuture<BuilderStatus> builderStatus() {
return SafeFuture.completedFuture(BuilderStatus.withOkStatus());
}

@Override
public SafeFuture<ExecutionPayloadHeader> getPayloadHeader(
final Bytes8 payloadId, final UInt64 slot) {
Expand Down Expand Up @@ -93,6 +99,8 @@ SafeFuture<TransitionConfiguration> engineExchangeTransitionConfiguration(
final TransitionConfiguration transitionConfiguration);

// builder namespace
SafeFuture<BuilderStatus> builderStatus();

SafeFuture<ExecutionPayloadHeader> getPayloadHeader(final Bytes8 payloadId, final UInt64 slot);

SafeFuture<ExecutionPayload> proposeBlindedBlock(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import tech.pegasys.teku.spec.SpecVersion;
import tech.pegasys.teku.spec.config.SpecConfigBellatrix;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.execution.BuilderStatus;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader;
import tech.pegasys.teku.spec.datastructures.execution.PowBlock;
Expand Down Expand Up @@ -256,6 +257,11 @@ public SafeFuture<TransitionConfiguration> engineExchangeTransitionConfiguration
return SafeFuture.completedFuture(transitionConfigurationResponse);
}

@Override
public SafeFuture<BuilderStatus> builderStatus() {
return SafeFuture.completedFuture(BuilderStatus.withOkStatus());
}

@Override
public SafeFuture<ExecutionPayloadHeader> getPayloadHeader(
final Bytes8 payloadId, final UInt64 slot) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
import org.hyperledger.besu.plugin.services.MetricsSystem;
import tech.pegasys.teku.ethereum.executionengine.ExecutionClientProvider;
import tech.pegasys.teku.ethereum.executionlayer.ExecutionLayerChannelImpl;
import tech.pegasys.teku.infrastructure.async.AsyncRunner;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.async.timed.RepeatingTaskScheduler;
import tech.pegasys.teku.infrastructure.events.EventChannels;
import tech.pegasys.teku.infrastructure.time.TimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.service.serviceutils.Service;
import tech.pegasys.teku.service.serviceutils.ServiceConfig;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel;
Expand All @@ -36,17 +39,15 @@ public class ExecutionLayerService extends Service {
private static final Logger LOG = LogManager.getLogger();

private final EventChannels eventChannels;
private final ExecutionLayerConfiguration config;
private final MetricsSystem metricsSystem;
private final ExecutionClientProvider engineWeb3jClientProvider;
private final Optional<ExecutionClientProvider> builderWeb3jClientProvider;
private final TimeProvider timeProvider;
private final ExecutionLayerChannel executionLayerChannel;
private final AsyncRunner asyncRunner;

public ExecutionLayerService(
final ServiceConfig serviceConfig, final ExecutionLayerConfiguration config) {
this.eventChannels = serviceConfig.getEventChannels();
this.metricsSystem = serviceConfig.getMetricsSystem();
this.config = config;
this.engineWeb3jClientProvider =
ExecutionClientProvider.create(
config.getEngineEndpoint(),
Expand All @@ -73,32 +74,43 @@ public ExecutionLayerService(
checkState(
engineWeb3jClientProvider.isStub() == builderIsStub,
"mixed configuration with stubbed and non-stubbed execution layer endpoints is not supported");

this.timeProvider = serviceConfig.getTimeProvider();
}
this.asyncRunner = serviceConfig.createAsyncRunner("execution-layer");

@Override
protected SafeFuture<?> doStart() {
final String endpoint = engineWeb3jClientProvider.getEndpoint();
LOG.info("Using execution engine at {}", endpoint);
final ExecutionLayerChannel executionLayerChannel;
if (engineWeb3jClientProvider.isStub()) {
EVENT_LOG.executionLayerStubEnabled();
executionLayerChannel = new ExecutionLayerChannelStub(config.getSpec(), timeProvider, true);
this.executionLayerChannel =
new ExecutionLayerChannelStub(config.getSpec(), timeProvider, true);
} else {
executionLayerChannel =
final MetricsSystem metricsSystem = serviceConfig.getMetricsSystem();
this.executionLayerChannel =
ExecutionLayerChannelImpl.create(
engineWeb3jClientProvider.getWeb3JClient(),
builderWeb3jClientProvider.map(ExecutionClientProvider::getWeb3JClient),
config.getEngineVersion(),
config.getSpec(),
metricsSystem);
}
}

@Override
protected SafeFuture<?> doStart() {
eventChannels.subscribe(ExecutionLayerChannel.class, executionLayerChannel);
final RepeatingTaskScheduler taskScheduler =
new RepeatingTaskScheduler(asyncRunner, timeProvider);
taskScheduler.scheduleRepeatingEvent(
timeProvider.getTimeInSeconds(),
UInt64.valueOf(30),
(scheduledTime, actualTime) -> performBuilderHealthCheck());
return SafeFuture.COMPLETE;
}

@Override
protected SafeFuture<?> doStop() {
asyncRunner.shutdown();
return SafeFuture.COMPLETE;
}

Expand All @@ -107,4 +119,21 @@ public Optional<ExecutionClientProvider> getEngineWeb3jClientProvider() {
? Optional.empty()
: Optional.of(engineWeb3jClientProvider);
}

private void performBuilderHealthCheck() {
if (builderWeb3jClientProvider.isEmpty()) {
return;
}
executionLayerChannel
.builderStatus()
.finish(
status -> {
if (status.hasFailed()) {
LOG.error(
"The execution builder has failed health check: {}", status.getErrorMessage());
}
},
throwable ->
LOG.error("Error while performing the execution builder health check", throwable));
}
}

0 comments on commit 6968acb

Please sign in to comment.