Skip to content

Commit ebd556a

Browse files
EIP-6110 Stop Eth1 data polling (Consensys#8141)
1 parent 129f0eb commit ebd556a

File tree

14 files changed

+408
-182
lines changed

14 files changed

+408
-182
lines changed

Diff for: beacon/pow/src/main/java/tech/pegasys/teku/beacon/pow/TimeBasedEth1HeadTracker.java

+41-38
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ public class TimeBasedEth1HeadTracker implements Eth1HeadTracker, RunLoopLogic {
4141

4242
private final Spec spec;
4343
private final TimeProvider timeProvider;
44-
private final AsyncRunner asyncRunner;
4544
private final Eth1Provider eth1Provider;
45+
private final AsyncRunLoop asyncRunLoop;
4646

4747
private final ObservableValue<UInt64> headSubscription = new ObservableValue<>(true);
4848

@@ -58,13 +58,29 @@ public TimeBasedEth1HeadTracker(
5858
final Eth1Provider eth1Provider) {
5959
this.spec = spec;
6060
this.timeProvider = timeProvider;
61-
this.asyncRunner = asyncRunner;
6261
this.eth1Provider = eth1Provider;
62+
this.asyncRunLoop =
63+
new AsyncRunLoop(this, asyncRunner, Constants.ETH1_DEPOSIT_REQUEST_RETRY_TIMEOUT);
6364
}
6465

6566
@Override
6667
public void start() {
67-
new AsyncRunLoop(this, asyncRunner, Constants.ETH1_DEPOSIT_REQUEST_RETRY_TIMEOUT).start();
68+
asyncRunLoop.start();
69+
}
70+
71+
@Override
72+
public void stop() {
73+
asyncRunLoop.stop();
74+
}
75+
76+
@Override
77+
public long subscribe(final ValueObserver<UInt64> subscriber) {
78+
return headSubscription.subscribe(subscriber);
79+
}
80+
81+
@Override
82+
public void unsubscribe(final long subscriberId) {
83+
headSubscription.unsubscribe(subscriberId);
6884
}
6985

7086
@Override
@@ -108,6 +124,28 @@ public SafeFuture<Void> advance() {
108124
}
109125
}
110126

127+
@Override
128+
public Duration getDelayUntilNextAdvance() {
129+
return Duration.ofSeconds(
130+
nextAdvanceTimeInSeconds.minusMinZero(timeProvider.getTimeInSeconds()).longValue());
131+
}
132+
133+
@Override
134+
public void onError(final Throwable t) {
135+
final Throwable rootCause = Throwables.getRootCause(t);
136+
if (rootCause instanceof BlockUnavailableException) {
137+
LOG.error(
138+
"Block number {} not yet available. Retrying after delay.",
139+
((BlockUnavailableException) rootCause).getBlockNumber());
140+
} else if (rootCause instanceof Eth1RequestException && rootCause.getSuppressed().length == 0) {
141+
LOG.debug("Failed to update eth1 chain head - no endpoints available");
142+
} else if (ExceptionUtil.hasCause(t, ConnectException.class)) {
143+
LOG.error("Failed to update eth1 chain head - {}", t.getMessage());
144+
} else {
145+
LOG.error("Failed to update eth1 chain head", t);
146+
}
147+
}
148+
111149
private SafeFuture<Void> stepForward() {
112150
LOG.trace("Searching forwards from block {}", nextCandidateHead.getNumber());
113151
if (isOldEnough(nextCandidateHead)) {
@@ -162,28 +200,6 @@ private SafeFuture<Block> getBlock(final UInt64 blockNumber) {
162200
maybeBlock -> maybeBlock.orElseThrow(() -> new BlockUnavailableException(blockNumber)));
163201
}
164202

165-
@Override
166-
public Duration getDelayUntilNextAdvance() {
167-
return Duration.ofSeconds(
168-
nextAdvanceTimeInSeconds.minusMinZero(timeProvider.getTimeInSeconds()).longValue());
169-
}
170-
171-
@Override
172-
public void onError(final Throwable t) {
173-
final Throwable rootCause = Throwables.getRootCause(t);
174-
if (rootCause instanceof BlockUnavailableException) {
175-
LOG.error(
176-
"Block number {} not yet available. Retrying after delay.",
177-
((BlockUnavailableException) rootCause).getBlockNumber());
178-
} else if (rootCause instanceof Eth1RequestException && rootCause.getSuppressed().length == 0) {
179-
LOG.debug("Failed to update eth1 chain head - no endpoints available");
180-
} else if (ExceptionUtil.hasCause(t, ConnectException.class)) {
181-
LOG.error("Failed to update eth1 chain head - {}", t.getMessage());
182-
} else {
183-
LOG.error("Failed to update eth1 chain head", t);
184-
}
185-
}
186-
187203
private boolean isOldEnough(final Block headBlock) {
188204
final UInt64 cutOffTime = getCutOffTime();
189205
final long blockTime = headBlock.getTimestamp().longValueExact();
@@ -206,9 +222,6 @@ private UInt64 getEth1FollowDistance() {
206222
return spec.getGenesisSpecConfig().getEth1FollowDistance();
207223
}
208224

209-
@Override
210-
public void stop() {}
211-
212225
private void notifyNewHead(final Block headBlock) {
213226
searchForwards = true;
214227
if (!headBlock.equals(lastNotifiedChainHead)) {
@@ -221,16 +234,6 @@ private void notifyNewHead(final Block headBlock) {
221234
}
222235
}
223236

224-
@Override
225-
public long subscribe(final ValueObserver<UInt64> subscriber) {
226-
return headSubscription.subscribe(subscriber);
227-
}
228-
229-
@Override
230-
public void unsubscribe(final long subscriberId) {
231-
headSubscription.unsubscribe(subscriberId);
232-
}
233-
234237
private static class BlockUnavailableException extends RuntimeException {
235238
private final UInt64 blockNumber;
236239

Diff for: beacon/validator/src/jmh/java/tech/pegasys/teku/validator/coordinator/DepositProviderBenchmark.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import java.util.List;
1919
import java.util.concurrent.TimeUnit;
20-
import java.util.stream.Collectors;
2120
import java.util.stream.IntStream;
2221
import java.util.stream.Stream;
2322
import org.apache.tuweni.bytes.Bytes32;
@@ -47,7 +46,7 @@ public class DepositProviderBenchmark {
4746
private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec);
4847
private final int depositEventCount = 1000;
4948

50-
private List<DepositsFromBlockEvent> events =
49+
private final List<DepositsFromBlockEvent> events =
5150
IntStream.range(0, depositEventCount)
5251
.mapToObj(
5352
index ->
@@ -62,14 +61,14 @@ public class DepositProviderBenchmark {
6261
dataStructureUtil.randomSignature(),
6362
spec.getGenesisSpecConfig().getMaxEffectiveBalance(),
6463
UInt64.valueOf(index)))))
65-
.collect(Collectors.toList());
64+
.toList();
6665

6766
private final NoOpMetricsSystem metricsSystem = new NoOpMetricsSystem();
6867
private final DepositProvider depositProvider =
6968
new DepositProvider(
7069
metricsSystem,
7170
mock(RecentChainData.class),
72-
new Eth1DataCache(metricsSystem, new Eth1VotingPeriod(spec)),
71+
new Eth1DataCache(spec, metricsSystem, new Eth1VotingPeriod(spec)),
7372
mock(StorageUpdateChannel.class),
7473
mock(Eth1DepositStorageChannel.class),
7574
spec,

Diff for: beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/DepositProvider.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ public void onNewFinalizedCheckpoint(
138138
.getEth1DataAndHeight(finalizedState.getEth1Data())
139139
.map(Eth1DataCache.Eth1DataAndHeight::getBlockHeight);
140140
if (heightOptional.isEmpty()) {
141-
LOG.debug("Eth1Data height not found in cache. Skipping DepositTree pruning");
141+
LOG.debug("Eth1Data height not found in cache. Skipping DepositTree finalization");
142142
return;
143143
}
144144
depositMerkleTree.finalize(finalizedState.getEth1Data(), heightOptional.get());
@@ -179,14 +179,16 @@ public void onSlot(final UInt64 slot) {
179179
return;
180180
}
181181

182-
// We want to verify our Beacon Node view of the eth1 deposits.
183-
// So we want to check if it has the necessary deposit data to propose a block
184-
185182
recentChainData
186183
.getBestState()
187184
.get()
188185
.thenAccept(
189186
state -> {
187+
if (spec.isFormerDepositMechanismDisabled(state)) {
188+
return;
189+
}
190+
// We want to verify our Beacon Node view of the eth1 deposits.
191+
// So we want to check if it has the necessary deposit data to propose a block
190192
final UInt64 eth1DepositCount = state.getEth1Data().getDepositCount();
191193

192194
final UInt64 lastAvailableDepositIndex =

Diff for: beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/Eth1DataCache.java

+20-8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import tech.pegasys.teku.infrastructure.metrics.SettableGauge;
2929
import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory;
3030
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
31+
import tech.pegasys.teku.spec.Spec;
3132
import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data;
3233
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
3334

@@ -39,8 +40,9 @@ public class Eth1DataCache {
3940
static final String VOTES_CURRENT_METRIC_NAME = "eth1_current_period_votes_current";
4041
static final String VOTES_BEST_METRIC_NAME = "eth1_current_period_votes_best";
4142

42-
private final UInt64 cacheDuration;
43+
private final Spec spec;
4344
private final Eth1VotingPeriod eth1VotingPeriod;
45+
private final UInt64 cacheDuration;
4446

4547
private final NavigableMap<UInt64, Eth1DataAndHeight> eth1ChainCache =
4648
new ConcurrentSkipListMap<>();
@@ -50,7 +52,9 @@ public class Eth1DataCache {
5052
private final SettableGauge currentPeriodVotesBest;
5153
private final SettableGauge currentPeriodVotesMax;
5254

53-
public Eth1DataCache(final MetricsSystem metricsSystem, final Eth1VotingPeriod eth1VotingPeriod) {
55+
public Eth1DataCache(
56+
final Spec spec, final MetricsSystem metricsSystem, final Eth1VotingPeriod eth1VotingPeriod) {
57+
this.spec = spec;
5458
this.eth1VotingPeriod = eth1VotingPeriod;
5559
cacheDuration = eth1VotingPeriod.getCacheDurationInSeconds();
5660
metricsSystem.createIntegerGauge(
@@ -110,17 +114,21 @@ public void onEth1Block(
110114
prune(blockTimestamp);
111115
}
112116

113-
public Eth1Data getEth1Vote(BeaconState state) {
114-
NavigableMap<UInt64, Eth1Data> votesToConsider =
117+
public Eth1Data getEth1Vote(final BeaconState state) {
118+
if (spec.isFormerDepositMechanismDisabled(state)) {
119+
// no need for a real vote when Eth1 polling has been disabled
120+
return state.getEth1Data();
121+
}
122+
final NavigableMap<UInt64, Eth1Data> votesToConsider =
115123
getVotesToConsider(state.getSlot(), state.getGenesisTime(), state.getEth1Data());
116124
// Avoid using .values() directly as it has O(n) lookup which gets expensive fast
117125
final Set<Eth1Data> validBlocks = new HashSet<>(votesToConsider.values());
118126
final Map<Eth1Data, Eth1Vote> votes = countVotes(state);
119127

120-
Eth1Data defaultVote =
128+
final Eth1Data defaultVote =
121129
votesToConsider.isEmpty() ? state.getEth1Data() : votesToConsider.lastEntry().getValue();
122130

123-
Optional<Eth1Data> vote =
131+
final Optional<Eth1Data> vote =
124132
votes.entrySet().stream()
125133
.filter(entry -> validBlocks.contains(entry.getKey()))
126134
.max(Map.Entry.comparingByValue())
@@ -134,12 +142,16 @@ public Collection<Eth1Data> getAllEth1Blocks() {
134142
}
135143

136144
public void updateMetrics(final BeaconState state) {
145+
if (spec.isFormerDepositMechanismDisabled(state)) {
146+
// no need to update metrics when Eth1 polling has been disabled
147+
return;
148+
}
137149
final Eth1Data currentEth1Data = state.getEth1Data();
138150
// Avoid using .values() directly as it has O(n) lookup which gets expensive fast
139151
final Set<Eth1Data> knownBlocks =
140152
new HashSet<>(
141153
getVotesToConsider(state.getSlot(), state.getGenesisTime(), currentEth1Data).values());
142-
Map<Eth1Data, Eth1Vote> votes = countVotes(state);
154+
final Map<Eth1Data, Eth1Vote> votes = countVotes(state);
143155

144156
currentPeriodVotesMax.set(eth1VotingPeriod.getTotalSlotsInVotingPeriod(state.getSlot()));
145157
currentPeriodVotesTotal.set(state.getEth1DataVotes().size());
@@ -156,7 +168,7 @@ public void updateMetrics(final BeaconState state) {
156168
}
157169

158170
protected Map<Eth1Data, Eth1Vote> countVotes(final BeaconState state) {
159-
Map<Eth1Data, Eth1Vote> votes = new HashMap<>();
171+
final Map<Eth1Data, Eth1Vote> votes = new HashMap<>();
160172
int i = 0;
161173
for (Eth1Data eth1Data : state.getEth1DataVotes()) {
162174
final int currentIndex = i;

Diff for: beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/DepositProviderTest.java

+15
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,21 @@ void shouldLogAnEventOnSlotWhenAllDepositsRequiredForStateNotAvailable() {
305305
verify(eventLogger).eth1DepositDataNotAvailable(UInt64.valueOf(9), UInt64.valueOf(10));
306306
}
307307

308+
@Test
309+
void
310+
shouldNotLogAnEventOnSlotIfFormerDepositMechanismIsDisabled_EvenIfAllDepositsRequiredForStateNotAvailable() {
311+
setup(1, SpecMilestone.ELECTRA);
312+
mockDepositsFromEth1Block(0, 8);
313+
updateStateEth1DepositIndex(5);
314+
updateStateDepositReceiptsStartIndex(5);
315+
updateStateEth1DataDepositCount(10);
316+
when(recentChainData.getBestState()).thenReturn(Optional.of(SafeFuture.completedFuture(state)));
317+
318+
depositProvider.onSlot(UInt64.ONE);
319+
320+
verifyNoInteractions(eventLogger);
321+
}
322+
308323
@Test
309324
void shouldNotLogAnEventOnSlotWhenAllDepositsRequiredForStateAvailable() {
310325
setup(1);

0 commit comments

Comments
 (0)