Skip to content

Commit

Permalink
[MEV Boost\Builder] Implement builder_status and health check (#5428)
Browse files Browse the repository at this point in the history
  • Loading branch information
StefanBratanov authored May 10, 2022
1 parent 2a09d6a commit fc7c3e2
Show file tree
Hide file tree
Showing 11 changed files with 371 additions and 45 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ dependencyRules {
allowed = [":infrastructure:unsigned"]
}

['core', 'dataproviders', 'events', 'executionengine', 'executionlayer', 'networks', 'pow', 'signingrecord', 'spec'].forEach( {
['core', 'dataproviders', 'events', 'executionclient', 'executionlayer', 'networks', 'pow', 'signingrecord', 'spec'].forEach( {
register(":ethereum:${it}") {
allowed = [
":infrastructure:",
Expand Down
6 changes: 6 additions & 0 deletions ethereum/executionlayer/build.gradle
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
dependencies {
implementation project(':ethereum:events')
implementation project(':ethereum:spec')
implementation project(':ethereum:executionclient')
implementation project(':infrastructure:async')
implementation project(':infrastructure:events')
implementation project(':infrastructure:exceptions')
implementation project(':infrastructure:logging')
implementation project(':infrastructure:metrics')
implementation project(':infrastructure:time')

testImplementation testFixtures(project(':infrastructure:async'))
testImplementation testFixtures(project(':ethereum:spec'))
}

publishing {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* 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.ethereum.executionlayer;

import tech.pegasys.teku.ethereum.events.SlotEventsChannel;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel;

public interface ExecutionLayerManager extends SlotEventsChannel, ExecutionLayerChannel {}
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static tech.pegasys.teku.infrastructure.logging.EventLogger.EVENT_LOG;
import static tech.pegasys.teku.spec.config.Constants.MAXIMUM_CONCURRENT_EB_REQUESTS;
import static tech.pegasys.teku.spec.config.Constants.MAXIMUM_CONCURRENT_EE_REQUESTS;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes32;
Expand All @@ -44,6 +46,7 @@
import tech.pegasys.teku.ethereum.executionclient.schema.TransitionConfigurationV1;
import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException;
import tech.pegasys.teku.infrastructure.logging.EventLogger;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
Expand All @@ -52,32 +55,36 @@
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeader;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadHeaderSchema;
import tech.pegasys.teku.spec.datastructures.execution.PowBlock;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel;
import tech.pegasys.teku.spec.executionlayer.ForkChoiceState;
import tech.pegasys.teku.spec.executionlayer.PayloadAttributes;
import tech.pegasys.teku.spec.executionlayer.PayloadStatus;
import tech.pegasys.teku.spec.executionlayer.TransitionConfiguration;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix;

public class ExecutionLayerChannelImpl implements ExecutionLayerChannel {
public class ExecutionLayerManagerImpl implements ExecutionLayerManager {
private static final Logger LOG = LogManager.getLogger();

private final ExecutionEngineClient executionEngineClient;
private final Optional<ExecutionBuilderClient> executionBuilderClient;

private final AtomicBoolean latestBuilderAvailability;

private final Spec spec;

public static ExecutionLayerChannelImpl create(
private final EventLogger eventLogger;

public static ExecutionLayerManagerImpl create(
final Web3JClient engineWeb3JClient,
final Optional<Web3JClient> builderWeb3JClient,
final Version version,
final Spec spec,
final MetricsSystem metricsSystem) {
checkNotNull(version);
return new ExecutionLayerChannelImpl(
return new ExecutionLayerManagerImpl(
createEngineClient(version, engineWeb3JClient, metricsSystem),
createBuilderClient(builderWeb3JClient, metricsSystem),
spec);
spec,
EVENT_LOG);
}

private static ExecutionEngineClient createEngineClient(
Expand All @@ -101,21 +108,21 @@ private static Optional<ExecutionBuilderClient> createBuilderClient(
metricsSystem)));
}

private ExecutionLayerChannelImpl(
ExecutionLayerManagerImpl(
final ExecutionEngineClient executionEngineClient,
final Optional<ExecutionBuilderClient> executionBuilderClient,
final Spec spec) {
this.spec = spec;
final Spec spec,
final EventLogger eventLogger) {
this.executionEngineClient = executionEngineClient;
this.executionBuilderClient = executionBuilderClient;
this.latestBuilderAvailability = new AtomicBoolean(executionBuilderClient.isPresent());
this.spec = spec;
this.eventLogger = eventLogger;
}

private static <K> K unwrapResponseOrThrow(Response<K> response) {
checkArgument(
response.getErrorMessage() == null,
"Invalid remote response: %s",
response.getErrorMessage());
return checkNotNull(response.getPayload(), "No payload content found");
@Override
public void onSlot(UInt64 slot) {
updateBuilderAvailability();
}

@Override
Expand Down Expand Up @@ -152,7 +159,7 @@ public SafeFuture<PowBlock> eth1GetPowChainHead() {
.forkChoiceUpdated(
ForkChoiceStateV1.fromInternalForkChoiceState(forkChoiceState),
PayloadAttributesV1.fromInternalForkChoiceState(payloadAttributes))
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionLayerManagerImpl::unwrapResponseOrThrow)
.thenApply(ForkChoiceUpdatedResult::asInternalExecutionPayload)
.thenPeek(
forkChoiceUpdatedResult ->
Expand All @@ -173,7 +180,7 @@ public SafeFuture<ExecutionPayload> engineGetPayload(

return executionEngineClient
.getPayload(executionPayloadContext.getPayloadId())
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionLayerManagerImpl::unwrapResponseOrThrow)
.thenCombine(
SafeFuture.of(
() ->
Expand All @@ -195,7 +202,7 @@ public SafeFuture<PayloadStatus> engineNewPayload(final ExecutionPayload executi

return executionEngineClient
.newPayload(ExecutionPayloadV1.fromInternalExecutionPayload(executionPayload))
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionLayerManagerImpl::unwrapResponseOrThrow)
.thenApply(PayloadStatusV1::asInternalExecutionPayload)
.thenPeek(
payloadStatus ->
Expand All @@ -214,7 +221,7 @@ public SafeFuture<TransitionConfiguration> engineExchangeTransitionConfiguration
return executionEngineClient
.exchangeTransitionConfiguration(
TransitionConfigurationV1.fromInternalTransitionConfiguration(transitionConfiguration))
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionLayerManagerImpl::unwrapResponseOrThrow)
.thenApply(TransitionConfigurationV1::asInternalTransitionConfiguration)
.thenPeek(
remoteTransitionConfiguration ->
Expand All @@ -224,6 +231,10 @@ public SafeFuture<TransitionConfiguration> engineExchangeTransitionConfiguration
remoteTransitionConfiguration));
}

boolean isBuilderAvailable() {
return latestBuilderAvailability.get();
}

@Override
public SafeFuture<ExecutionPayloadHeader> builderGetHeader(
final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) {
Expand All @@ -238,7 +249,7 @@ public SafeFuture<ExecutionPayloadHeader> builderGetHeader(
return executionBuilderClient
.get()
.getHeader(slot, Bytes48.ZERO, executionPayloadContext.getParentHash())
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionLayerManagerImpl::unwrapResponseOrThrow)
.thenApply(
builderBidV1SignedMessage ->
getExecutionHeaderFromBuilderBid(builderBidV1SignedMessage, slot))
Expand Down Expand Up @@ -282,7 +293,7 @@ public SafeFuture<ExecutionPayload> builderGetPayload(
new SignedMessage<>(
new BlindedBeaconBlockV1(signedBlindedBeaconBlock.getMessage()),
signedBlindedBeaconBlock.getSignature()))
.thenApply(ExecutionLayerChannelImpl::unwrapResponseOrThrow)
.thenApply(ExecutionLayerManagerImpl::unwrapResponseOrThrow)
.thenCombine(
SafeFuture.of(
() ->
Expand All @@ -297,4 +308,37 @@ public SafeFuture<ExecutionPayload> builderGetPayload(
signedBlindedBeaconBlock,
executionPayload));
}

private static <K> K unwrapResponseOrThrow(Response<K> response) {
checkArgument(
response.getErrorMessage() == null,
"Invalid remote response: %s",
response.getErrorMessage());
return checkNotNull(response.getPayload(), "No payload content found");
}

private void updateBuilderAvailability() {
if (executionBuilderClient.isEmpty()) {
return;
}
executionBuilderClient
.get()
.status()
.finish(
statusResponse -> {
if (statusResponse.getErrorMessage() != null) {
markBuilderAsNotAvailable(statusResponse.getErrorMessage());
} else {
if (latestBuilderAvailability.compareAndSet(false, true)) {
eventLogger.executionBuilderIsBackOnline();
}
}
},
throwable -> markBuilderAsNotAvailable(throwable.getMessage()));
}

private void markBuilderAsNotAvailable(String errorMessage) {
latestBuilderAvailability.set(false);
eventLogger.executionBuilderIsOffline(errorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.ethereum.executionlayer;

import tech.pegasys.teku.infrastructure.time.TimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannelStub;

public class ExecutionLayerManagerStub extends ExecutionLayerChannelStub
implements ExecutionLayerManager {

public ExecutionLayerManagerStub(
Spec spec, TimeProvider timeProvider, boolean enableTransitionEmulation) {
super(spec, timeProvider, enableTransitionEmulation);
}

@Override
public void onSlot(UInt64 slot) {
// NOOP
}
}
Loading

0 comments on commit fc7c3e2

Please sign in to comment.