Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MEV Boost\Builder] Implement builder_status and health check #5428

Merged
merged 9 commits into from
May 10, 2022
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