Skip to content

Commit 808909e

Browse files
authored
Updated computeSubnetForAttestation (#9872)
During non finality it's possible to have long periods without slots, and in these cases we would need to advance the state slot to be able to compute the attestation subnet. Ordinarily this code will not be in range to be used, but we will need to start computing epochs to ensure we're in range. This was found by diamond tests scenario nfin-001
1 parent 3edcac1 commit 808909e

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed

ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import java.util.function.Function;
3838
import java.util.function.Supplier;
3939
import javax.annotation.CheckReturnValue;
40+
import org.apache.logging.log4j.LogManager;
41+
import org.apache.logging.log4j.Logger;
4042
import org.apache.tuweni.bytes.Bytes;
4143
import org.apache.tuweni.bytes.Bytes32;
4244
import tech.pegasys.teku.bls.BLSPublicKey;
@@ -112,6 +114,7 @@
112114
import tech.pegasys.teku.spec.schemas.registry.SchemaRegistryBuilder;
113115

114116
public class Spec {
117+
private static final Logger LOG = LogManager.getLogger();
115118
private final Map<SpecMilestone, SpecVersion> specVersions;
116119
private final ForkSchedule forkSchedule;
117120
private final StateTransition stateTransition;
@@ -626,6 +629,28 @@ public UInt64 computeNextEpochBoundary(final UInt64 slot) {
626629
}
627630

628631
public int computeSubnetForAttestation(final BeaconState state, final Attestation attestation) {
632+
final UInt64 stateEpoch = getCurrentEpoch(state);
633+
final UInt64 attestationEpoch =
634+
atEpoch(stateEpoch).miscHelpers().computeEpochAtSlot(attestation.getData().getSlot());
635+
final UInt64 earliestEpochInRange =
636+
attestationEpoch.minusMinZero(atEpoch(attestationEpoch).getConfig().getMaxSeedLookahead());
637+
if (stateEpoch.isLessThan(earliestEpochInRange)) {
638+
final UInt64 earliestSlot =
639+
atEpoch(stateEpoch).miscHelpers().computeStartSlotAtEpoch(earliestEpochInRange);
640+
LOG.debug(
641+
"Processing empty slots for state at slot {}, target slot {}",
642+
state.getSlot(),
643+
earliestSlot);
644+
try {
645+
final BeaconState advancedState = processSlots(state, earliestSlot);
646+
return atState(advancedState)
647+
.getBeaconStateUtil()
648+
.computeSubnetForAttestation(advancedState, attestation);
649+
} catch (SlotProcessingException | EpochProcessingException e) {
650+
LOG.debug("Couldn't wind state at slot {} forward", state.getSlot(), e);
651+
}
652+
}
653+
629654
return atState(state).getBeaconStateUtil().computeSubnetForAttestation(state, attestation);
630655
}
631656

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright Consensys Software Inc., 2025
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package tech.pegasys.teku.spec;
15+
16+
import static org.assertj.core.api.Assertions.assertThat;
17+
18+
import java.util.List;
19+
import java.util.Optional;
20+
import org.junit.jupiter.api.Test;
21+
import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState;
22+
import tech.pegasys.teku.spec.generator.ChainBuilder;
23+
import tech.pegasys.teku.spec.util.DataStructureUtil;
24+
import tech.pegasys.teku.storage.server.StateStorageMode;
25+
import tech.pegasys.teku.storage.storageSystem.InMemoryStorageSystemBuilder;
26+
import tech.pegasys.teku.storage.storageSystem.StorageSystem;
27+
28+
class SpecTest {
29+
final Spec spec = TestSpecFactory.createMinimalAltair();
30+
final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec);
31+
private final StorageSystem storageSystem =
32+
InMemoryStorageSystemBuilder.buildDefault(StateStorageMode.PRUNE, spec);
33+
private final ChainBuilder chainBuilder = storageSystem.chainBuilder();
34+
35+
@Test
36+
void shouldWindStateForwardIfOutsidePeriod() {
37+
chainBuilder.generateGenesis();
38+
chainBuilder.generateBlocksUpToSlot(8);
39+
final List<SignedBlockAndState> chain = chainBuilder.finalizeCurrentChain(Optional.empty());
40+
41+
// the important thing here is that we get a response, we don't fail, even though slot 100 is
42+
// well beyond the finalized slot (in epoch 4)
43+
assertThat(
44+
spec.computeSubnetForAttestation(
45+
chain.getLast().getState(), dataStructureUtil.randomAttestation(100)))
46+
.isEqualTo(48);
47+
}
48+
}

0 commit comments

Comments
 (0)