Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f9442d7
Check gas limit consistency with the target
zilm13 May 19, 2026
ac74eb6
restore merge deleted test
zilm13 May 28, 2026
9823b60
remove unneeded mocks
zilm13 May 28, 2026
a17ca40
Alpha.8 reference tests
zilm13 May 20, 2026
80b9688
remove hidden executor test skips
zilm13 May 28, 2026
980ca15
add temporary exceptions for specrefs
zilm13 May 28, 2026
d9c1a59
Merge branch 'master' into reftests-alpha8
zilm13 May 29, 2026
8786fc3
enable gloas reference tests
zilm13 May 29, 2026
475fb3b
specrefs alpha.8 todo later
zilm13 May 29, 2026
201c042
add missing is_within_slot_range added later to phase0
gfukushima Jun 1, 2026
37123dc
refactor the isAttestationSlotInCurrentOrPreviousEpoch to use new mis…
gfukushima Jun 1, 2026
7eaa2ff
storeBlockMetaData loads slot from block not from state
gfukushima Jun 1, 2026
5e89904
enable attestation and aggregate suites
gfukushima Jun 1, 2026
ab771fb
use similar approach for creating anchor and checkpoint and override …
gfukushima Jun 1, 2026
1708a1a
add aggregate committee bits validation
gfukushima Jun 1, 2026
8fdf205
add unit test for aggregation committee bits validation bit
gfukushima Jun 1, 2026
6736dbc
refactor to test to to show we're using genesisTime in seconds
gfukushima Jun 1, 2026
08fdb96
add validation for when attestation/aggregation is not descendent of …
gfukushima Jun 1, 2026
4bf51c8
Merge branch 'master' into reftests-att-and-aggregate
gfukushima Jun 1, 2026
0fded54
Merge branch 'master' into reftests-att-and-aggregate
zilm13 Jun 1, 2026
0b2af45
remove duplicates
zilm13 Jun 1, 2026
14cb80c
addressing PR review comments
gfukushima Jun 2, 2026
ac45250
Merge branch 'master' into reftests-att-and-aggregate
gfukushima Jun 2, 2026
157963a
remove unused param
gfukushima Jun 2, 2026
beb9744
Merge remote-tracking branch 'origin/reftests-att-and-aggregate' into…
gfukushima Jun 2, 2026
654f967
spotless
gfukushima Jun 2, 2026
0d66c80
Merge branch 'master' into reftests-att-and-aggregate
gfukushima Jun 2, 2026
2222de0
fix gossip test and add gossip blob sidecar test executor
gfukushima Jun 2, 2026
17bc0e7
add a skip for gossip manager subnet validation
gfukushima Jun 2, 2026
77abd1b
remove changes commited accidentally in the wrong branch
gfukushima Jun 2, 2026
3770929
Merge remote-tracking branch 'origin/reftests-att-and-aggregate' into…
gfukushima Jun 2, 2026
31709b5
spotless
gfukushima Jun 2, 2026
870adfc
add similar mechanism to override the checkpoint descendent check whi…
gfukushima Jun 4, 2026
4c8c187
Merge branch 'master' into reftests-att-and-aggregate
gfukushima Jun 4, 2026
a896462
add check against invalidBlock map for attestations as per spec, foll…
gfukushima Jun 4, 2026
db861c5
Merge remote-tracking branch 'origin/reftests-att-and-aggregate' into…
gfukushima Jun 4, 2026
62ba614
Merge branch 'master' into reftests-att-and-aggregate
gfukushima Jun 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import java.util.Optional;
import java.util.function.Function;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.xerial.snappy.Snappy;
import org.yaml.snakeyaml.LoaderOptions;
import tech.pegasys.teku.ethtests.finder.TestDefinition;
Expand Down Expand Up @@ -149,12 +150,28 @@ public static AnchorPoint createAnchorFromState(final Spec spec, final BeaconSta
*/
public static AnchorPoint createAnchorFromStateAndMatchingBlock(
final Spec spec, final BeaconState state, final Collection<SignedBeaconBlock> blocks) {
final Optional<AnchorPoint> anchorFromMatchingStateRoot =
blocks.stream()
.filter(block -> block.getStateRoot().equals(state.hashTreeRoot()))
.findFirst()
.map(
block ->
AnchorPoint.fromInitialBlockAndState(
spec, new SignedBlockAndState(block, state)));
if (anchorFromMatchingStateRoot.isPresent()) {
return anchorFromMatchingStateRoot.get();
}

final Bytes32 stateBlockRoot = BeaconBlockHeader.fromState(state).hashTreeRoot();
return blocks.stream()
.filter(block -> block.getStateRoot().equals(state.hashTreeRoot()))
.filter(block -> block.getRoot().equals(stateBlockRoot))
.findFirst()
.map(
block ->
AnchorPoint.fromInitialBlockAndState(spec, new SignedBlockAndState(block, state)))
block -> {
final UInt64 epoch = spec.computeNextEpochBoundary(state.getSlot());
final Checkpoint checkpoint = new Checkpoint(epoch, block.getRoot());
return AnchorPoint.create(spec, checkpoint, state, Optional.of(block));
})
.orElseGet(() -> createAnchorFromState(spec, state));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.HashSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.tuweni.bytes.Bytes32;
import tech.pegasys.teku.bls.BLSSignatureVerifier;
import tech.pegasys.teku.ethtests.finder.TestDefinition;
Expand All @@ -39,6 +39,7 @@
import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock;
import tech.pegasys.teku.spec.datastructures.operations.SignedAggregateAndProof;
import tech.pegasys.teku.spec.datastructures.state.AnchorPoint;
import tech.pegasys.teku.spec.datastructures.state.Checkpoint;
import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState;
import tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannelStub;
import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult;
Expand All @@ -60,6 +61,7 @@
import tech.pegasys.teku.storage.server.StateStorageMode;
import tech.pegasys.teku.storage.storageSystem.InMemoryStorageSystemBuilder;
import tech.pegasys.teku.storage.storageSystem.StorageSystem;
import tech.pegasys.teku.storage.store.UpdatableStore;

public class GossipBeaconAggregateAndProofTestExecutor implements TestExecutor {

Expand Down Expand Up @@ -124,24 +126,21 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {

forkChoice.onTick(UInt64.valueOf(metaData.getCurrentTimeMs()), Optional.empty());

// Track block roots that explicitly failed validation (marked failed: true in meta.yaml).
// We load these blocks to obtain their hash tree root but do not import them, mirroring the
// spec distinction between "block not seen" (IGNORE) and "block failed validation" (REJECT).
final Set<Bytes32> failedBlockRoots = new HashSet<>();
// Blocks marked failed: true in meta.yaml are recorded as invalid (by root) and not imported,
// mirroring the invalidBlockRoots map maintained in production by BlockManager. The attestation
// validator rejects aggregates voting for one of these roots.
final Map<Bytes32, BlockImportResult> invalidBlockRoots = new HashMap<>();

// When a custom finalized_checkpoint is set, its root will not be in the chain, so any block
// import attempt would fail. Skip all imports so that the validator's block-not-available path
// produces the expected IGNORE result.
final boolean hasCustomFinalizedCheckpoint = metaData.getFinalizedCheckpoint() != null;
for (final BlockEntryAndBlock blockEntryAndBlock : blocks) {
final GossipBeaconAggregateAndProofMetaData.BlockEntry blockEntry =
blockEntryAndBlock.blockEntry();
final SignedBeaconBlock block = blockEntryAndBlock.block();
if (blockEntry.isFailed()) {
// Record the root of this invalid block so aggregates voting for it are rejected.
// Don't import it — a NOOP BLS verifier would accept it despite the bad signature.
failedBlockRoots.add(block.getRoot());
} else if (!hasCustomFinalizedCheckpoint && !block.getRoot().equals(anchorPoint.getRoot())) {
invalidBlockRoots.put(
block.getRoot(), BlockImportResult.FAILED_DESCENDANT_OF_INVALID_BLOCK);
} else if (!block.getRoot().equals(anchorPoint.getRoot())) {
final BlockImportResult importResult =
safeJoin(
forkChoice.onBlock(
Expand All @@ -152,11 +151,28 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {
}
}

Optional<Checkpoint> customFinalizedCheckpoint = Optional.empty();
if (metaData.getFinalizedCheckpoint() != null) {
final GossipBeaconAggregateAndProofMetaData.FinalizedCheckpoint finalizedCheckpoint =
metaData.getFinalizedCheckpoint();
if (finalizedCheckpoint.getBlock() != null) {
final Checkpoint checkpoint = finalizedCheckpoint.toCheckpoint(testDefinition, spec);
final UpdatableStore.StoreTransaction tx = recentChainData.startStoreTransaction();
tx.setFinalizedCheckpoint(checkpoint, false);
safeJoin(tx.commit());
} else {
customFinalizedCheckpoint =
Optional.of(finalizedCheckpoint.toCheckpoint(testDefinition, spec));
}
}

final AttestationValidator attestationValidator =
new AttestationValidator(
spec,
AsyncBLSSignatureVerifier.wrap(blsVerifier),
new GossipValidationHelper(spec, recentChainData, metricsSystem));
createGossipValidationHelper(
spec, recentChainData, metricsSystem, customFinalizedCheckpoint),
invalidBlockRoots);
final AggregateAttestationValidator aggregateValidator =
new AggregateAttestationValidator(
spec, attestationValidator, AsyncBLSSignatureVerifier.wrap(blsVerifier));
Expand All @@ -172,18 +188,6 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {
message.getMessage() + ".ssz_snappy",
spec.getGenesisSchemaDefinitions().getSignedAggregateAndProofSchema());

// Failed-block check: aggregate votes for a block that failed validation
final Bytes32 votedBlockRoot =
signedAggregateAndProof.getMessage().getAggregate().getData().getBeaconBlockRoot();
if (failedBlockRoots.contains(votedBlockRoot)) {
assertThat(message.getExpected())
.describedAs(
"Expected reject for aggregate %s voting for failed block %s",
message.getMessage(), votedBlockRoot)
.isEqualTo("reject");
continue;
}

final ValidatableAttestation validatableAttestation =
ValidatableAttestation.aggregateFromNetwork(spec, signedAggregateAndProof);
final InternalValidationResult result =
Expand Down Expand Up @@ -218,6 +222,29 @@ public void runTest(final TestDefinition testDefinition) throws Throwable {
}
}

private static GossipValidationHelper createGossipValidationHelper(
final Spec spec,
final RecentChainData recentChainData,
final StubMetricsSystem metricsSystem,
final Optional<Checkpoint> finalizedCheckpointOverride) {
return finalizedCheckpointOverride
.<GossipValidationHelper>map(
finalizedCheckpoint ->
new GossipValidationHelper(spec, recentChainData, metricsSystem) {
@Override
public boolean currentFinalizedCheckpointIsAncestorOfAttestationBlock(
final Bytes32 blockRoot) {
return spec.getAncestor(
getForkChoiceStrategy(),
blockRoot,
finalizedCheckpoint.getEpochStartSlot(spec))
.map(ancestorRoot -> ancestorRoot.equals(finalizedCheckpoint.getRoot()))
.orElse(false);
}
})
.orElseGet(() -> new GossipValidationHelper(spec, recentChainData, metricsSystem));
}

@SuppressWarnings("unused")
@JsonIgnoreProperties(ignoreUnknown = true)
private static class GossipBeaconAggregateAndProofMetaData {
Expand All @@ -238,7 +265,7 @@ private static class GossipBeaconAggregateAndProofMetaData {
private int blsSetting;

@JsonProperty(value = "finalized_checkpoint", required = false)
private Object finalizedCheckpoint;
private FinalizedCheckpoint finalizedCheckpoint;

public List<BlockEntry> getBlocks() {
return blocks;
Expand All @@ -256,7 +283,7 @@ public BlsSetting getBlsSetting() {
return BlsSetting.forCode(blsSetting);
}

public Object getFinalizedCheckpoint() {
public FinalizedCheckpoint getFinalizedCheckpoint() {
return finalizedCheckpoint;
}

Expand Down Expand Up @@ -305,6 +332,38 @@ public String getExpected() {
return expected;
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
private static class FinalizedCheckpoint {

@JsonProperty(value = "epoch", required = true)
private long epoch;

@JsonProperty(value = "root")
private String root;

@JsonProperty(value = "block")
private String block;

public String getBlock() {
return block;
}

public Checkpoint toCheckpoint(final TestDefinition testDefinition, final Spec spec) {
final Bytes32 checkpointRoot;
if (root != null) {
checkpointRoot = Bytes32.fromHexString(root);
} else if (block != null) {
final SignedBeaconBlock signedBlock =
loadSsz(testDefinition, block + ".ssz_snappy", spec::deserializeSignedBeaconBlock);
checkpointRoot = signedBlock.getRoot();
} else {
throw new IllegalStateException(
"finalized_checkpoint must specify either 'root' or 'block'");
}
return new Checkpoint(UInt64.valueOf(epoch), checkpointRoot);
}
}
}

private record BlockEntryAndBlock(
Expand Down
Loading
Loading