diff --git a/CHANGELOG.md b/CHANGELOG.md index 381ed9f94bf..6cfc17e9e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,11 @@ the [releases page](https://github.com/Consensys/teku/releases). ### Additions and Improvements - Introduced [Validator Slashing Prevention feature](https://docs.teku.consensys.io/how-to/prevent-slashing/detect-slashing). - If the EL supports the `engine_getClientVersionV1` Engine API method, the default graffiti (when no graffiti has been configured by the validator) will include EL as well as CL version information. For more details, please see https://github.com/ethereum/execution-apis/pull/517. -- `—p2p-private-key-file` command line option supports reading a binary private key file. +- `--p2p-private-key-file` command line option supports reading a binary private key file. - Updated libp2p seen cache configuration to reflect EIP-7045 spec changes. This reduces CPU and network bandwidth consumption. -- Increased the attestation cache capacity to allow teku a bigger pool of attestations when block building. +- Increased the attestation cache capacity to allow Teku a bigger pool of attestations when block building. +- Defaulted `--builder-bid-compare-factor` to 90. This makes it necessary for external block builders to give at least 10% additional profit compared to a local build before being taken into consideration. If you would like to go back to the previous default, set `--builder-bid-compare-factor` to 100. +- Added `--p2p-direct-peers` command line option to configure explicit peers as per [Explicit Peering Agreements](https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#explicit-peering-agreements) libp2p spec. ### Bug Fixes +- Fix incompatibility between Teku validator client and Lighthouse beacon nodes [#8117](https://github.com/Consensys/teku/pull/8117) diff --git a/acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuBeaconNode.java b/acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuBeaconNode.java index baac1dd7d38..cec4c8294f7 100644 --- a/acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuBeaconNode.java +++ b/acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuBeaconNode.java @@ -707,12 +707,7 @@ public void waitForAttestationBeingGossiped( Set attesterIndicesInAttestations = block.getMessage().getBody().attestations.stream() - .map( - a -> - spec.getAttestingIndices( - state, - a.asInternalAttestation(spec).getData(), - a.asInternalAttestation(spec).getAggregationBits())) + .map(a -> spec.getAttestingIndices(state, a.asInternalAttestation(spec))) .flatMap(Collection::stream) .map(UInt64::valueOf) .collect(toSet()); diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java index 1f7d73209eb..610069b46c1 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactory.java @@ -13,7 +13,8 @@ package tech.pegasys.teku.validator.coordinator; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkState; + import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -117,9 +118,7 @@ public Function> createSelector( final SszList attestations = attestationPool.getAttestationsForBlock( - blockSlotState, - new AttestationForkChecker(spec, blockSlotState), - spec.createAttestationWorthinessChecker(blockSlotState)); + blockSlotState, new AttestationForkChecker(spec, blockSlotState)); // Collect slashings to include final Set exitedValidators = new HashSet<>(); @@ -453,20 +452,28 @@ public Function> createBlobSidecarsSelec final SszList blobs; final SszList proofs; + final SszList blockCommitments = + block.getMessage().getBody().getOptionalBlobKzgCommitments().orElseThrow(); if (blockContainer.isBlinded()) { // need to use the builder BlobsBundle for the blinded flow, because the // blobs and the proofs wouldn't be part of the BlockContainer final tech.pegasys.teku.spec.datastructures.builder.BlobsBundle blobsBundle = getCachedBuilderBlobsBundle(slot); + + blobs = blobsBundle.getBlobs(); + proofs = blobsBundle.getProofs(); + // consistency check because the BlobsBundle comes from an external source (a builder) - final SszList blockCommitments = - block.getMessage().getBody().getOptionalBlobKzgCommitments().orElseThrow(); - Preconditions.checkState( + checkState( blobsBundle.getCommitments().hashTreeRoot().equals(blockCommitments.hashTreeRoot()), "Commitments in the builder BlobsBundle don't match the commitments in the block"); - blobs = blobsBundle.getBlobs(); - proofs = blobsBundle.getProofs(); + checkState( + blockCommitments.size() == proofs.size(), + "The number of proofs in BlobsBundle doesn't match the number of commitments in the block"); + checkState( + blockCommitments.size() == blobs.size(), + "The number of blobs in BlobsBundle doesn't match the number of commitments in the block"); } else { blobs = blockContainer.getBlobs().orElseThrow(); proofs = blockContainer.getKzgProofs().orElseThrow(); diff --git a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/DepositProvider.java b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/DepositProvider.java index cfc72391eba..dac6bd34e0d 100644 --- a/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/DepositProvider.java +++ b/beacon/validator/src/main/java/tech/pegasys/teku/validator/coordinator/DepositProvider.java @@ -74,8 +74,8 @@ public class DepositProvider private boolean inSync = false; public DepositProvider( - MetricsSystem metricsSystem, - RecentChainData recentChainData, + final MetricsSystem metricsSystem, + final RecentChainData recentChainData, final Eth1DataCache eth1DataCache, final StorageUpdateChannel storageUpdateChannel, final Eth1DepositStorageChannel eth1DepositStorageChannel, @@ -99,7 +99,7 @@ public DepositProvider( } @Override - public synchronized void onDepositsFromBlock(DepositsFromBlockEvent event) { + public synchronized void onDepositsFromBlock(final DepositsFromBlockEvent event) { event.getDeposits().stream() .map(depositUtil::convertDepositEventToOperationDeposit) .forEach( @@ -171,7 +171,7 @@ public void onEth1Block( } @Override - public void onMinGenesisTimeBlock(MinGenesisTimeBlockEvent event) {} + public void onMinGenesisTimeBlock(final MinGenesisTimeBlockEvent event) {} @Override public void onSlot(final UInt64 slot) { @@ -201,33 +201,54 @@ public void onSlot(final UInt64 slot) { .ifExceptionGetsHereRaiseABug(); } - public void onSyncingStatusChanged(boolean inSync) { + public void onSyncingStatusChanged(final boolean inSync) { this.inSync = inSync; } - public synchronized SszList getDeposits(BeaconState state, Eth1Data eth1Data) { + public synchronized SszList getDeposits( + final BeaconState state, final Eth1Data eth1Data) { + final long maxDeposits = spec.getMaxDeposits(state); + final SszListSchema depositsSchema = depositsSchemaCache.get(maxDeposits); + // no Eth1 deposits needed if already transitioned to the EIP-6110 mechanism + if (spec.isFormerDepositMechanismDisabled(state)) { + return depositsSchema.createFromElements(emptyList()); + } final UInt64 eth1DepositCount; if (spec.isEnoughVotesToUpdateEth1Data(state, eth1Data, 1)) { eth1DepositCount = eth1Data.getDepositCount(); } else { eth1DepositCount = state.getEth1Data().getDepositCount(); } - final UInt64 eth1DepositIndex = state.getEth1DepositIndex(); + final UInt64 eth1PendingDepositCount = + state + .toVersionElectra() + .map( + stateElectra -> { + // EIP-6110 + final UInt64 eth1DepositIndexLimit = + eth1DepositCount.min(stateElectra.getDepositReceiptsStartIndex()); + return eth1DepositIndexLimit.minusMinZero(eth1DepositIndex).min(maxDeposits); + }) + .orElseGet( + () -> { + // Phase0 + return eth1DepositCount.minusMinZero(eth1DepositIndex).min(maxDeposits); + }); + + // No deposits to include + if (eth1PendingDepositCount.isZero()) { + return depositsSchema.createFromElements(emptyList()); + } + // We need to have all the deposits that can be included in the state available to ensure // the generated proofs are valid checkRequiredDepositsAvailable(eth1DepositCount, eth1DepositIndex); - final long maxDeposits = spec.getMaxDeposits(state); - final UInt64 latestDepositIndexWithMaxBlock = eth1DepositIndex.plus(spec.getMaxDeposits(state)); - - final UInt64 toDepositIndex = - latestDepositIndexWithMaxBlock.isGreaterThan(eth1DepositCount) - ? eth1DepositCount - : latestDepositIndexWithMaxBlock; + final UInt64 toDepositIndex = eth1DepositIndex.plus(eth1PendingDepositCount); - return getDepositsWithProof(eth1DepositIndex, toDepositIndex, eth1DepositCount, maxDeposits); + return getDepositsWithProof(eth1DepositIndex, toDepositIndex, eth1DepositCount, depositsSchema); } protected synchronized List getAvailableDeposits() { @@ -261,14 +282,12 @@ public synchronized int getDepositMapSize() { * @param eth1DepositCount number of deposits in the merkle tree according to Eth1Data in state */ private SszList getDepositsWithProof( - UInt64 fromDepositIndex, UInt64 toDepositIndex, UInt64 eth1DepositCount, long maxDeposits) { + final UInt64 fromDepositIndex, + final UInt64 toDepositIndex, + final UInt64 eth1DepositCount, + final SszListSchema depositsSchema) { final AtomicReference expectedDepositIndex = new AtomicReference<>(fromDepositIndex); - final SszListSchema depositsSchema = depositsSchemaCache.get(maxDeposits); final SszBytes32VectorSchema depositProofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); - // No deposits to include so don't bother rewinding the merkle tree. - if (fromDepositIndex.equals(toDepositIndex)) { - return depositsSchema.createFromElements(emptyList()); - } if (depositMerkleTree.getDepositCount() < eth1DepositCount.intValue()) { throw MissingDepositsException.missingRange( UInt64.valueOf(depositMerkleTree.getDepositCount()), eth1DepositCount); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java index 045ed39e5e2..c4c72093395 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/AbstractBlockFactoryTest.java @@ -185,7 +185,7 @@ protected BlockContainerAndMetaData assertBlockCreated( final BlockFactory blockFactory = createBlockFactory(spec); when(depositProvider.getDeposits(any(), any())).thenReturn(deposits); - when(attestationsPool.getAttestationsForBlock(any(), any(), any())).thenReturn(attestations); + when(attestationsPool.getAttestationsForBlock(any(), any())).thenReturn(attestations); when(attesterSlashingPool.getItemsForBlock(any(), any(), any())).thenReturn(attesterSlashings); when(proposerSlashingPool.getItemsForBlock(any(), any(), any())).thenReturn(proposerSlashings); when(voluntaryExitPool.getItemsForBlock(any(), any(), any())).thenReturn(voluntaryExits); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java index 7094d834dc0..7efce12065c 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/BlockOperationSelectorFactoryTest.java @@ -17,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.assertThatSafeFuture; import static tech.pegasys.teku.infrastructure.async.SafeFutureAssert.safeJoin; @@ -211,7 +212,7 @@ class BlockOperationSelectorFactoryTest { @BeforeEach void setUp() { - when(attestationPool.getAttestationsForBlock(any(), any(), any())) + when(attestationPool.getAttestationsForBlock(any(), any())) .thenReturn( beaconBlockSchemaSupplier.apply(UInt64.ZERO).getAttestationsSchema().getDefault()); when(contributionValidator.validate(any())).thenReturn(SafeFuture.completedFuture(ACCEPT)); @@ -819,7 +820,7 @@ void shouldCreateBlobSidecarsForBlockContents() { } @Test - void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleIsNotConsistent() { + void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleCommitmentsRootIsNotConsistent() { final SszList commitments = dataStructureUtil.randomBlobKzgCommitments(3); final SignedBeaconBlock signedBlindedBeaconBlock = dataStructureUtil.randomSignedBlindedBeaconBlockWithCommitments(commitments); @@ -838,6 +839,48 @@ void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleIsNotConsistent() { "Commitments in the builder BlobsBundle don't match the commitments in the block"); } + @Test + void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleProofsIsNotConsistent() { + final SszList commitments = dataStructureUtil.randomBlobKzgCommitments(3); + final SignedBeaconBlock signedBlindedBeaconBlock = + dataStructureUtil.randomSignedBlindedBeaconBlockWithCommitments(commitments); + + final tech.pegasys.teku.spec.datastructures.builder.BlobsBundle blobsBundle = + spy(dataStructureUtil.randomBuilderBlobsBundle(commitments)); + when(blobsBundle.getBlobs()).thenReturn(dataStructureUtil.randomSszBlobs(2)); + + prepareCachedBuilderPayload( + signedBlindedBeaconBlock.getSlot(), + dataStructureUtil.randomExecutionPayload(), + Optional.of(blobsBundle)); + + assertThatThrownBy(() -> factory.createBlobSidecarsSelector().apply(signedBlindedBeaconBlock)) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "The number of blobs in BlobsBundle doesn't match the number of commitments in the block"); + } + + @Test + void shouldFailCreatingBlobSidecarsIfBuilderBlobsBundleBlobsIsNotConsistent() { + final SszList commitments = dataStructureUtil.randomBlobKzgCommitments(3); + final SignedBeaconBlock signedBlindedBeaconBlock = + dataStructureUtil.randomSignedBlindedBeaconBlockWithCommitments(commitments); + + final tech.pegasys.teku.spec.datastructures.builder.BlobsBundle blobsBundle = + spy(dataStructureUtil.randomBuilderBlobsBundle(commitments)); + when(blobsBundle.getProofs()).thenReturn(dataStructureUtil.randomSszKZGProofs(2)); + + prepareCachedBuilderPayload( + signedBlindedBeaconBlock.getSlot(), + dataStructureUtil.randomExecutionPayload(), + Optional.of(blobsBundle)); + + assertThatThrownBy(() -> factory.createBlobSidecarsSelector().apply(signedBlindedBeaconBlock)) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "The number of proofs in BlobsBundle doesn't match the number of commitments in the block"); + } + @Test void shouldCreateBlobSidecarsForBlindedBlock() { final SszList commitments = dataStructureUtil.randomBlobKzgCommitments(3); diff --git a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/DepositProviderTest.java b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/DepositProviderTest.java index 8ce844fe380..c595a4cb0f5 100644 --- a/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/DepositProviderTest.java +++ b/beacon/validator/src/test/java/tech/pegasys/teku/validator/coordinator/DepositProviderTest.java @@ -40,10 +40,10 @@ import tech.pegasys.teku.infrastructure.ssz.schema.SszListSchema; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.SpecVersion; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.config.SpecConfig; -import tech.pegasys.teku.spec.config.SpecConfigLoader; import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data; import tech.pegasys.teku.spec.datastructures.operations.Deposit; import tech.pegasys.teku.spec.datastructures.operations.DepositData; @@ -51,8 +51,10 @@ 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.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; import tech.pegasys.teku.spec.datastructures.util.DepositUtil; import tech.pegasys.teku.spec.datastructures.util.MerkleTree; +import tech.pegasys.teku.spec.networks.Eth2Network; import tech.pegasys.teku.spec.util.DataStructureUtil; import tech.pegasys.teku.storage.api.Eth1DepositStorageChannel; import tech.pegasys.teku.storage.api.StorageUpdateChannel; @@ -64,7 +66,7 @@ public class DepositProviderTest { private Spec spec; private DataStructureUtil dataStructureUtil; private final RecentChainData recentChainData = mock(RecentChainData.class); - private final BeaconState state = mock(BeaconState.class); + private BeaconState state; private final Eth1DataCache eth1DataCache = mock(Eth1DataCache.class); private final StorageUpdateChannel storageUpdateChannel = mock(StorageUpdateChannel.class); private final Eth1DepositStorageChannel eth1DepositStorageChannel = @@ -78,12 +80,21 @@ public class DepositProviderTest { private DepositUtil depositUtil; void setup(final int maxDeposits) { - when(state.getSlot()).thenReturn(UInt64.valueOf(1234)); + setup(maxDeposits, SpecMilestone.PHASE0); + } - SpecConfig specConfig = SpecConfigLoader.loadConfig("minimal", b -> b.maxDeposits(maxDeposits)); - spec = TestSpecFactory.createPhase0(specConfig); + void setup(final int maxDeposits, final SpecMilestone milestone) { + spec = TestSpecFactory.create(milestone, Eth2Network.MINIMAL, b -> b.maxDeposits(maxDeposits)); depositUtil = new DepositUtil(spec); dataStructureUtil = new DataStructureUtil(spec); + state = + dataStructureUtil + .randomBeaconState(UInt64.valueOf(1234)) + .updated( + mutableState -> + // no votes + mutableState.setEth1DataVotes( + SszListSchema.create(Eth1Data.SSZ_SCHEMA, 32).of())); depositProvider = new DepositProvider( new StubMetricsSystem(), @@ -96,7 +107,6 @@ void setup(final int maxDeposits) { true); depositProvider.onSyncingStatusChanged(true); depositMerkleTree = new MerkleTree(spec.getGenesisSpecConfig().getDepositContractTreeDepth()); - mockStateEth1DataVotes(); createDepositEvents(40); randomEth1Data = dataStructureUtil.randomEth1Data(); } @@ -104,8 +114,8 @@ void setup(final int maxDeposits) { @Test void stateEth1DepositIndexIsEqualToEth1DataDepositCount_NoDepositReturned() { setup(5); - mockStateEth1DepositIndex(2); - mockEth1DataDepositCount(2); + updateStateEth1DepositIndex(2); + updateStateEth1DataDepositCount(2); mockDepositsFromEth1Block(0, 10); SszList deposits = depositProvider.getDeposits(state, randomEth1Data); assertThat(deposits).isEmpty(); @@ -114,8 +124,8 @@ void stateEth1DepositIndexIsEqualToEth1DataDepositCount_NoDepositReturned() { @Test void numberOfDepositsThatCanBeIncludedLessThanMaxDeposits() { setup(16); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(20); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(20); mockDepositsFromEth1Block(0, 10); mockDepositsFromEth1Block(10, 20); @@ -128,8 +138,8 @@ void numberOfDepositsThatCanBeIncludedLessThanMaxDeposits() { @Test void numberOfDepositsGetsAdjustedAccordingToOurEth1DataVote() { setup(30); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(20); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(20); mockDepositsFromEth1Block(0, 10); mockDepositsFromEth1Block(10, 30); @@ -142,8 +152,8 @@ void numberOfDepositsGetsAdjustedAccordingToOurEth1DataVote() { SszList et1hDataVotes = Stream.generate(() -> newEth1Data) .limit(enoughVoteCount) - .collect(SszListSchema.create(Eth1Data.SSZ_SCHEMA, 50).collector()); - when(state.getEth1DataVotes()).thenReturn(et1hDataVotes); + .collect(SszListSchema.create(Eth1Data.SSZ_SCHEMA, 32).collector()); + state = state.updated(mutableState -> mutableState.setEth1DataVotes(et1hDataVotes)); SszList deposits = depositProvider.getDeposits(state, newEth1Data); assertThat(deposits).hasSize(25); @@ -153,8 +163,8 @@ void numberOfDepositsGetsAdjustedAccordingToOurEth1DataVote() { @Test void numberOfDepositsThatCanBeIncludedMoreThanMaxDeposits() { setup(10); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(20); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(20); mockDepositsFromEth1Block(0, 10); mockDepositsFromEth1Block(10, 20); @@ -164,18 +174,49 @@ void numberOfDepositsThatCanBeIncludedMoreThanMaxDeposits() { checkThatDepositProofIsValid(deposits); } + @Test + void noDepositsIncludedIfFormerDepositMechanismHasBeenDisabled() { + setup(16, SpecMilestone.ELECTRA); + updateStateEth1DepositIndex(5); + updateStateDepositReceiptsStartIndex(5); + + final SszList deposits = depositProvider.getDeposits(state, randomEth1Data); + + assertThat(deposits).isEmpty(); + } + + @Test + void getsRemainingEth1PendingDepositsIfElectraIsEnabled() { + setup(16, SpecMilestone.ELECTRA); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(20); + // 16th deposit is using the new mechanism + updateStateDepositReceiptsStartIndex(16); + + mockDepositsFromEth1Block(0, 10); + mockDepositsFromEth1Block(10, 20); + + final SszList deposits = depositProvider.getDeposits(state, randomEth1Data); + + // the pending Eth1 deposits (deposit_receipt_start_index - eth1_deposit_index) + // we need to process eth1_deposit_index deposit (5) up to 16 (exclusive) so 11 is the + // expected size + assertThat(deposits).hasSize(11); + checkThatDepositProofIsValid(deposits); + } + @Test void depositsWithFinalizedIndicesGetPrunedFromMap() { setup(16); Bytes32 finalizedBlockRoot = Bytes32.fromHexString("0x01"); - mockStateEth1DepositIndex(10); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(10); + updateStateEth1DataDepositCount(10); mockDepositsFromEth1Block(0, 20); final AnchorPoint anchorPoint = mock(AnchorPoint.class); final UpdatableStore store = mock(UpdatableStore.class); when(recentChainData.getStore()).thenReturn(store); when(store.getLatestFinalized()).thenReturn(anchorPoint); - when(anchorPoint.getState()).thenReturn(state); + when(anchorPoint.getState()).thenAnswer(__ -> state); assertThat(depositProvider.getDepositMapSize()).isEqualTo(20); @@ -219,8 +260,8 @@ void shouldNotifyEth1DataCacheOfDepositBlocks() { @Test void shouldNotThrowMissingDepositsExceptionWhenAllKnownDepositsHaveBeenIncluded() { setup(16); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(5); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(5); mockDepositsFromEth1Block(0, 5); assertThat(depositProvider.getDeposits(state, randomEth1Data)).isEmpty(); } @@ -228,8 +269,8 @@ void shouldNotThrowMissingDepositsExceptionWhenAllKnownDepositsHaveBeenIncluded( @Test void shouldThrowMissingDepositsExceptionWhenRequiredDepositsAreNotAvailable() { setup(16); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(10); assertThatThrownBy(() -> depositProvider.getDeposits(state, randomEth1Data)) .isInstanceOf(MissingDepositsException.class) .hasMessageContaining("6 to 10"); @@ -241,8 +282,8 @@ void shouldThrowMissingDepositsExceptionWhenAllDepositsRequiredForStateNotAvaila // To generate a valid proof we need the deposits up to state deposit count // So fail even if we could have filled MAX_DEPOSITS mockDepositsFromEth1Block(0, 8); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(10); assertThatThrownBy(() -> depositProvider.getDeposits(state, randomEth1Data)) .isInstanceOf(MissingDepositsException.class) @@ -255,8 +296,8 @@ void shouldLogAnEventOnSlotWhenAllDepositsRequiredForStateNotAvailable() { // To generate a valid proof we need the deposits up to state deposit count // So we want to check if on each slot our node has necessary deposit data mockDepositsFromEth1Block(0, 8); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(10); when(recentChainData.getBestState()).thenReturn(Optional.of(SafeFuture.completedFuture(state))); depositProvider.onSlot(UInt64.ONE); @@ -270,8 +311,8 @@ void shouldNotLogAnEventOnSlotWhenAllDepositsRequiredForStateAvailable() { // To generate a valid proof we need the deposits up to state deposit count // So we want to check if on each slot our node has necessary deposit data mockDepositsFromEth1Block(0, 10); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(10); when(recentChainData.getBestState()).thenReturn(Optional.of(SafeFuture.completedFuture(state))); depositProvider.onSlot(UInt64.ONE); @@ -285,8 +326,8 @@ void shouldThrowWhenAllDepositsRequiredForStateNotAvailable_skippedDeposit() { mockDepositsFromEth1Block(0, 7); // Deposit 7 is missing mockDepositsFromEth1Block(8, 10); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(10); assertThatThrownBy(() -> depositProvider.getDeposits(state, randomEth1Data)) .isInstanceOf(MissingDepositsException.class) @@ -299,8 +340,8 @@ void shouldThrowWhenAllDepositsRequiredForStateNotAvailable_skippedDeposits() { mockDepositsFromEth1Block(0, 7); // Deposits 7,8 are missing mockDepositsFromEth1Block(9, 10); - mockStateEth1DepositIndex(5); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(5); + updateStateEth1DataDepositCount(10); assertThatThrownBy(() -> depositProvider.getDeposits(state, randomEth1Data)) .isInstanceOf(MissingDepositsException.class) @@ -310,7 +351,7 @@ void shouldThrowWhenAllDepositsRequiredForStateNotAvailable_skippedDeposits() { @Test void whenCallingAvailableDeposits_AllDepositReturned() { setup(5); - mockStateEth1DepositIndex(10); + updateStateEth1DepositIndex(10); mockDepositsFromEth1Block(0, 10); List deposits = depositProvider.getAvailableDeposits(); assertThat(deposits.size()).isEqualTo(10); @@ -319,9 +360,9 @@ void whenCallingAvailableDeposits_AllDepositReturned() { @Test void whenCallingAvailableDepositsAndSomeDepositsAlreadyInState_AllDepositsReturned() { setup(10); - mockStateEth1DepositIndex(2); + updateStateEth1DepositIndex(2); mockDepositsFromEth1Block(0, 10); - mockEth1DataDepositCount(10); + updateStateEth1DataDepositCount(10); SszList deposits = depositProvider.getDeposits(state, randomEth1Data); assertThat(deposits.size()).isEqualTo(8); List availableDeposits = depositProvider.getAvailableDeposits(); @@ -332,14 +373,14 @@ void whenCallingAvailableDepositsAndSomeDepositsAlreadyInState_AllDepositsReturn void whenCallingAvailableDepositsAndSomeDepositsPruned_AllNotPrunedDepositsReturned() { setup(16); Bytes32 finalizedBlockRoot = Bytes32.fromHexString("0x01"); - mockStateEth1DepositIndex(10); - mockEth1DataDepositCount(10); + updateStateEth1DepositIndex(10); + updateStateEth1DataDepositCount(10); mockDepositsFromEth1Block(0, 20); final AnchorPoint anchorPoint = mock(AnchorPoint.class); final UpdatableStore store = mock(UpdatableStore.class); when(recentChainData.getStore()).thenReturn(store); when(store.getLatestFinalized()).thenReturn(anchorPoint); - when(anchorPoint.getState()).thenReturn(state); + when(anchorPoint.getState()).thenAnswer(__ -> state); assertThat(depositProvider.getDepositMapSize()).isEqualTo(20); @@ -364,15 +405,15 @@ void whenCallingForFinalizedSnapshotAndSnapshotAvailable_SnapshotReturned() { dataStructureUtil.randomBytes32(), UInt64.valueOf(10), dataStructureUtil.randomBytes32()); - when(state.getEth1Data()).thenReturn(eth1Data1); + updateStateEth1Data(eth1Data1); Bytes32 finalizedBlockRoot = Bytes32.fromHexString("0x01"); - mockStateEth1DepositIndex(10); + updateStateEth1DepositIndex(10); mockDepositsFromEth1Block(0, 20); final AnchorPoint anchorPoint = mock(AnchorPoint.class); final UpdatableStore store = mock(UpdatableStore.class); when(recentChainData.getStore()).thenReturn(store); when(store.getLatestFinalized()).thenReturn(anchorPoint); - when(anchorPoint.getState()).thenReturn(state); + when(anchorPoint.getState()).thenAnswer(__ -> state); assertThat(depositProvider.getDepositMapSize()).isEqualTo(20); @@ -408,8 +449,8 @@ void whenCallingForFinalizedSnapshotAndSnapshotAvailable_SnapshotReturned() { dataStructureUtil.randomBytes32(), UInt64.valueOf(20), dataStructureUtil.randomBytes32()); - mockStateEth1DepositIndex(20); - when(state.getEth1Data()).thenReturn(eth1Data2); + updateStateEth1DepositIndex(20); + updateStateEth1Data(eth1Data2); when(eth1DataCache.getEth1DataAndHeight(eq(eth1Data2))) .thenReturn( Optional.of(new Eth1DataCache.Eth1DataAndHeight(eth1Data2, UInt64.valueOf(30)))); @@ -464,18 +505,28 @@ private void mockDepositsFromEth1Block(int startIndex, int n) { depositProvider.onDepositsFromBlock(depositsFromBlockEvent); } - private void mockEth1DataDepositCount(int n) { - Eth1Data eth1Data = mock(Eth1Data.class); - when(state.getEth1Data()).thenReturn(eth1Data); - when(eth1Data.getBlockHash()).thenReturn(dataStructureUtil.randomBytes32()); - when(eth1Data.getDepositCount()).thenReturn(UInt64.valueOf(n)); + private void updateStateEth1Data(Eth1Data eth1Data) { + state = state.updated(mutableState -> mutableState.setEth1Data(eth1Data)); + } + + private void updateStateEth1DataDepositCount(int n) { + final Eth1Data eth1Data = + new Eth1Data( + dataStructureUtil.randomBytes32(), + UInt64.valueOf(n), + dataStructureUtil.randomBytes32()); + updateStateEth1Data(eth1Data); } - private void mockStateEth1DepositIndex(int n) { - when(state.getEth1DepositIndex()).thenReturn(UInt64.valueOf(n)); + private void updateStateEth1DepositIndex(int n) { + state = state.updated(mutableState -> mutableState.setEth1DepositIndex(UInt64.valueOf(n))); } - private void mockStateEth1DataVotes() { - when(state.getEth1DataVotes()).thenReturn(SszListSchema.create(Eth1Data.SSZ_SCHEMA, 0).of()); + private void updateStateDepositReceiptsStartIndex(int n) { + state = + state.updated( + mutableState -> + MutableBeaconStateElectra.required(mutableState) + .setDepositReceiptsStartIndex(UInt64.valueOf(n))); } } diff --git a/build.gradle b/build.gradle index 39289169157..5124105b7f4 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version '6.25.0' id 'com.github.ben-manes.versions' version '0.51.0' - id 'com.github.jk1.dependency-license-report' version '2.5' + id 'com.github.jk1.dependency-license-report' version '2.6' id 'io.spring.dependency-management' version '1.1.4' id 'net.ltgt.errorprone' version '3.1.0' apply false id 'de.undercouch.download' version '5.6.0' diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/tekuv1/beacon/GetDepositSnapshotIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/tekuv1/beacon/GetDepositSnapshotIntegrationTest.java index 6b539a53014..6d35839e1d4 100644 --- a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/tekuv1/beacon/GetDepositSnapshotIntegrationTest.java +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/tekuv1/beacon/GetDepositSnapshotIntegrationTest.java @@ -26,7 +26,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.beaconrestapi.AbstractDataBackedRestAPIIntegrationTest; -import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon.GetDepositSnapshot; +import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetDepositSnapshot; import tech.pegasys.teku.ethereum.pow.api.DepositTreeSnapshot; import tech.pegasys.teku.infrastructure.http.ContentTypes; import tech.pegasys.teku.infrastructure.json.JsonUtil; diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blinded_blocks.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blinded_blocks.json index 7cf28015cd1..3a257f0a796 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blinded_blocks.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blinded_blocks.json @@ -4,6 +4,15 @@ "operationId" : "publishBlindedBlock", "summary" : "Publish a signed blinded block", "description" : "Submit a signed blinded beacon block to the beacon node to be broadcast and imported. The beacon node performs the required validation.", + "parameters" : [ { + "name" : "Eth-Consensus-Version", + "in" : "header", + "schema" : { + "type" : "string", + "enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ], + "description" : "Version of the block being submitted, if using SSZ encoding." + } + } ], "requestBody" : { "content" : { "application/octet-stream" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blocks.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blocks.json index f98d7e10620..85e3ef12d04 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blocks.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_blocks.json @@ -4,6 +4,15 @@ "operationId" : "publishBlock", "summary" : "Publish a signed block", "description" : "Submit a signed beacon block to the beacon node to be broadcast and imported. After Deneb, this additionally instructs the beacon node to broadcast and import all given blobs. The beacon node performs the required validation.", + "parameters" : [ { + "name" : "Eth-Consensus-Version", + "in" : "header", + "schema" : { + "type" : "string", + "enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ], + "description" : "Version of the block being submitted, if using SSZ encoding." + } + } ], "requestBody" : { "content" : { "application/octet-stream" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attestations.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attestations.json index fb796a2af07..49ca1d93cf0 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attestations.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attestations.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Beacon" ], - "operationId" : "getAttestations", - "summary" : "Get attestations", + "operationId" : "getPoolAttestations", + "summary" : "Get Attestations from operations pool", "description" : "Retrieves attestations known by the node but not necessarily incorporated into any block.", "parameters" : [ { "name" : "slot", @@ -58,8 +58,8 @@ }, "post" : { "tags" : [ "Beacon", "Validator Required Api" ], - "operationId" : "postAttestation", - "summary" : "Submit signed attestations", + "operationId" : "submitPoolAttestations", + "summary" : "Submit Attestation objects to node", "description" : "Submit signed attestations to the beacon node to be validated and submitted if valid.\n\nThis endpoint does not protected against slashing.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attester_slashings.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attester_slashings.json index 935330498c6..1cb019152a4 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attester_slashings.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_attester_slashings.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Beacon" ], - "operationId" : "getAttesterSlashings", - "summary" : "Get Attester Slashings", + "operationId" : "getPoolAttesterSlashings", + "summary" : "Get AttesterSlashings from operations pool", "description" : "Retrieves attester slashings known by the node but not necessarily incorporated into any block.", "responses" : { "200" : { @@ -39,8 +39,8 @@ }, "post" : { "tags" : [ "Beacon" ], - "operationId" : "postAttesterSlashing", - "summary" : "Submit attester slashing object", + "operationId" : "submitPoolAttesterSlashings", + "summary" : "Submit AttesterSlashing object to node's pool", "description" : "Submits attester slashing object to node's pool and if passes validation node MUST broadcast it to network.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_bls_to_execution_changes.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_bls_to_execution_changes.json index 0516b9ae8a5..88720ddd09b 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_bls_to_execution_changes.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_bls_to_execution_changes.json @@ -1,7 +1,7 @@ { "post" : { "tags" : [ "Beacon" ], - "operationId" : "postBlsToExecutionChange", + "operationId" : "submitPoolBLSToExecutionChange", "summary" : "Submit SignedBLSToExecutionChange object to node's pool", "description" : "Submits SignedBLSToExecutionChange object to node's pool and if passes validation node MUST broadcast it to network.", "requestBody" : { @@ -45,7 +45,7 @@ }, "get" : { "tags" : [ "Beacon" ], - "operationId" : "getBlsToExecutionChanges", + "operationId" : "getPoolBLSToExecutionChanges", "summary" : "Get SignedBLSToExecutionChange from operations pool", "description" : "Retrieves BLS to execution changes known by the node but not necessarily incorporated into any block", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_proposer_slashings.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_proposer_slashings.json index ca9f1291f2f..fde87dec5fc 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_proposer_slashings.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_proposer_slashings.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Beacon" ], - "operationId" : "getProposerSlashings", - "summary" : "Get proposer slashings", + "operationId" : "getPoolProposerSlashings", + "summary" : "Get ProposerSlashings from operations pool", "description" : "Retrieves proposer slashings known by the node but not necessarily incorporated into any block.", "responses" : { "200" : { @@ -39,8 +39,8 @@ }, "post" : { "tags" : [ "Beacon" ], - "operationId" : "postProposerSlashing", - "summary" : "Submit proposer slashing object", + "operationId" : "submitPoolProposerSlashings", + "summary" : "Submit ProposerSlashing object to node's pool", "description" : "Submits proposer slashing object to node's pool and, if it passes validation, the node MUST broadcast it to network.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_sync_committees.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_sync_committees.json index de62a2f5c1b..b596eedc4b4 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_sync_committees.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_sync_committees.json @@ -1,8 +1,8 @@ { "post" : { "tags" : [ "Beacon", "Validator Required Api" ], - "operationId" : "postSyncCommittees", - "summary" : "Submit sync committee messages to node", + "operationId" : "submitPoolSyncCommitteeSignatures", + "summary" : "Submit sync committee signatures to node", "description" : "Submits sync committee message objects to the node.\n\nSync committee messages are not present in phase0, but are required for Altair networks.\n\nIf a sync committee message is validated successfully the node MUST publish that sync committee message on all applicable subnets.\n\nIf one or more sync committee messages fail validation the node MUST return a 400 error with details of which sync committee messages have failed, and why.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_voluntary_exits.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_voluntary_exits.json index a303dc70d1a..b52ad4abd07 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_voluntary_exits.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_pool_voluntary_exits.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Beacon" ], - "operationId" : "getVoluntaryExits", - "summary" : "Get signed voluntary exits", + "operationId" : "getPoolVoluntaryExits", + "summary" : "Get SignedVoluntaryExit from operations pool", "description" : "Retrieves voluntary exits known by the node but not necessarily incorporated into any block.", "responses" : { "200" : { @@ -39,8 +39,8 @@ }, "post" : { "tags" : [ "Beacon" ], - "operationId" : "postVoluntaryExit", - "summary" : "Submit signed voluntary exit", + "operationId" : "submitPoolVoluntaryExit", + "summary" : "Submit SignedVoluntaryExit object to node's pool", "description" : "Submits signed voluntary exit object to node's pool and if it passes validation node MUST broadcast it to network.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_attestations_{epoch}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_attestations_{epoch}.json index ab090b1d860..d93f1135d5d 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_attestations_{epoch}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_attestations_{epoch}.json @@ -5,7 +5,7 @@ "Rewards" ], "operationId": "getAttestationsRewards", - "summary": "Get Attestations Rewards", + "summary": "Get attestations rewards", "description": "Retrieve attestation reward info for validators specified by array of public keys or validator index. If no array is provided, return reward info for every validator.", "parameters": [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_blocks_{block_id}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_blocks_{block_id}.json index 5688a0cd44c..5f463d97310 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_blocks_{block_id}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_blocks_{block_id}.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Beacon", "Rewards"], "operationId" : "getBlockRewards", - "summary" : "Get Block Rewards", + "summary" : "Get block rewards", "description" : "Retrieve block reward info for a single block.", "parameters" : [ { "name" : "block_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_sync_committee_{block_id}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_sync_committee_{block_id}.json index 2719d75f0b5..ba65d242dd7 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_sync_committee_{block_id}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_rewards_sync_committee_{block_id}.json @@ -2,7 +2,7 @@ "post" : { "tags" : [ "Beacon", "Rewards" ], "operationId" : "getSyncCommitteeRewards", - "summary" : "Get Sync Committee Rewards", + "summary" : "Get sync committee rewards", "description" : "Retrieves rewards info for sync committee members specified by array of public keys or validator index. If no array is provided, return reward info for every committee member.", "parameters" : [ { "name" : "block_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_committees.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_committees.json index 733033eddd5..02cbc3a9d05 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_committees.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_committees.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Beacon" ], - "operationId" : "getStateCommittees", - "summary" : "Get committees at state", + "operationId" : "getEpochCommittees", + "summary" : "Get all committees for a state.", "description" : "Retrieves the committees for the given state.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_fork.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_fork.json index a346d015eb4..92555750486 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_fork.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_fork.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Beacon", "Validator Required Api" ], - "operationId" : "getSateFork", - "summary" : "Get state fork", + "operationId" : "getStateFork", + "summary" : "Get Fork object for requested state", "description" : "Returns Fork object for state with given 'state_id'.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_randao.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_randao.json index d0a6e542427..9ed20d8fcf4 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_randao.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_randao.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Beacon" ], "operationId" : "getStateRandao", - "summary" : "Get state RANDAO", + "summary" : "Get the RANDAO mix for some epoch in a specified state.", "description" : "Fetch the RANDAO mix for the requested epoch from the state identified by `state_id`.\n\nIf an epoch is not specified then the RANDAO mix for the state's current epoch will be returned.\n\nBy adjusting the `state_id` parameter you can query for any historic value of the RANDAO mix. Ordinarily states from the same epoch will mutate the RANDAO mix for that epoch as blocks are applied.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_root.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_root.json index 8ad36c312b6..6e00296b7d6 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_root.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_root.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Beacon" ], "operationId" : "getStateRoot", - "summary" : "Get state root", + "summary" : "Get state SSZ HashTreeRoot", "description" : "Calculates HashTreeRoot for state with given 'state_id'. If stateId is root, same value will be returned.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_sync_committees.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_sync_committees.json index 5644f682cd5..47a13f66bb2 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_sync_committees.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_sync_committees.json @@ -1,8 +1,8 @@ { "get" : { - "tags" : [ "Beacon", "Validator Required Api" ], - "operationId" : "getStateSyncCommittees", - "summary" : "Get sync committees", + "tags" : [ "Beacon" ], + "operationId" : "getEpochSyncCommittees", + "summary" : "Get sync committees for a state.", "description" : "Retrieves the sync committees for the given state.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_validators_{validator_id}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_validators_{validator_id}.json index 0a7b5558176..b0917077fc1 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_validators_{validator_id}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_beacon_states_{state_id}_validators_{validator_id}.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Beacon", "Validator Required Api" ], "operationId" : "getStateValidator", - "summary" : "Get validator from state", + "summary" : "Get validator from state by id", "description" : "Retrieves data about the given peer.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_builder_states_{state_id}_expected_withdrawals.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_builder_states_{state_id}_expected_withdrawals.json index 9aba8d14945..05c9c548c8c 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_builder_states_{state_id}_expected_withdrawals.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_builder_states_{state_id}_expected_withdrawals.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Builder" ], - "operationId" : "GetExpectedWithdrawals", - "summary" : "Get Expected Withdrawals", + "operationId" : "getNextWithdrawals", + "summary" : "Get the withdrawals that are to be included for the block built on the specified state.", "description" : "Get the withdrawals computed from the specified state, that will be included in the block \n that gets built on the specified state.", "parameters" : [ { "name" : "state_id", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_deposit_contract.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_deposit_contract.json index f909cda4fe4..d4b7d6b2f79 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_deposit_contract.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_deposit_contract.json @@ -1,7 +1,7 @@ { "get" : { "tags" : [ "Config" ], - "operationId" : "getDepositContractAddress", + "operationId" : "getDepositContract", "summary" : "Get deposit contract address", "description" : "Retrieve deposit contract address and genesis fork version.", "responses" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_fork_schedule.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_fork_schedule.json index b51710c74d2..073b6bdc825 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_fork_schedule.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_fork_schedule.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Config" ], - "operationId" : "getScheduledForks", - "summary" : "Get scheduled forks", + "operationId" : "getForkSchedule", + "summary" : "Get scheduled upcoming forks.", "description" : "Retrieve all scheduled upcoming forks this node is aware of.", "responses" : { "200" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_spec.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_spec.json index 17ce8c7ddb5..762e59a38cf 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_spec.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_config_spec.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Config", "Validator Required Api" ], "operationId" : "getSpec", - "summary" : "Get spec params", + "summary" : "Get spec params.", "description" : "Retrieve specification configuration used on this node.", "responses" : { "200" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_health.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_health.json index 48df5e173f2..2efe4b39f74 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_health.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_health.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Node" ], - "operationId" : "GetNodeHealth", - "summary" : "Get node health", + "operationId" : "getHealth", + "summary" : "Get health check", "description" : "Returns node health status in http status codes. Useful for load balancers.", "parameters" : [ { "name" : "syncing_status", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers.json index 5c49f9c574a..7a7bd123e3b 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Node" ], - "operationId" : "getNodePeers", - "summary" : "Get node peers", + "operationId" : "getPeers", + "summary" : "Get node network peers", "description" : "Retrieves data about the node's network peers.", "responses" : { "200" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers_{peer_id}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers_{peer_id}.json index bface3a7d39..615870d888e 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers_{peer_id}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_peers_{peer_id}.json @@ -1,7 +1,7 @@ { "get" : { "tags" : [ "Node" ], - "operationId" : "getNodePeer", + "operationId" : "getPeer", "summary" : "Get node peer", "description" : "Retrieves data about the given peer.", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_syncing.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_syncing.json index 41a8ad509bd..51a66de992a 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_syncing.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_syncing.json @@ -1,7 +1,7 @@ { "get" : { "tags" : [ "Node", "Validator Required Api" ], - "operationId" : "getNodeSyncingStatus", + "operationId" : "getSyncingStatus", "summary" : "Get node syncing status", "description" : "Requests the beacon node to describe if it's currently syncing or not, and if it is, what block it is up to.", "responses" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_version.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_version.json index 81ea0bc7f4f..086fb548da3 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_version.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_node_version.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Node" ], "operationId" : "getNodeVersion", - "summary" : "Get node version", + "summary" : "Get version string of the running beacon node.", "description" : "similar to [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3).", "responses" : { "200" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_and_proofs.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_and_proofs.json index 9156abd137b..b6a236f1eba 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_and_proofs.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_and_proofs.json @@ -1,8 +1,8 @@ { "post" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "postAggregateAndProofs", - "summary" : "Publish aggregate and proofs", + "operationId" : "publishAggregateAndProofs", + "summary" : "Publish multiple aggregate and proofs", "description" : "Verifies given aggregate and proofs and publishes it on appropriate gossipsub topic.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_attestation.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_attestation.json index 0656545d37c..7c227e015fe 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_attestation.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_aggregate_attestation.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "getAggregateAttestation", - "summary" : "Get aggregated attestations", + "operationId" : "getAggregatedAttestation", + "summary" : "Get aggregated attestation", "description" : "Aggregates all attestations matching given attestation data root and slot.", "parameters" : [ { "name" : "attestation_data_root", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_attestation_data.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_attestation_data.json index 61fd3f07216..a9505fd14a0 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_attestation_data.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_attestation_data.json @@ -1,7 +1,7 @@ { "get" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "getAttestationData", + "operationId" : "produceAttestationData", "summary" : "Produce an AttestationData", "description" : "Requests that the beacon node produce an AttestationData.", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_beacon_committee_subscriptions.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_beacon_committee_subscriptions.json index 4fd5b22b5ad..b5551e1b368 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_beacon_committee_subscriptions.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_beacon_committee_subscriptions.json @@ -1,8 +1,8 @@ { "post" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "postSubscribeToBeaconCommitteeSubnet", - "summary" : "Subscribe to a committee subnet", + "operationId" : "prepareBeaconCommitteeSubnet", + "summary" : "Signal beacon node to prepare for a committee subnet", "description" : "After Beacon node receives this request, search using discv5 for peers related to this subnet and replace current peers with those ones if necessary If validator is_aggregator, beacon node must:\n- announce subnet topic subscription on gossipsub\n- aggregate attestations received on that subnet\n", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_blinded_blocks_{slot}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_blinded_blocks_{slot}.json index 4c59a3fd61c..154aefed021 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_blinded_blocks_{slot}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_blinded_blocks_{slot}.json @@ -1,7 +1,7 @@ { "get" : { "tags" : [ "Validator", "Validator Required Api"], - "operationId" : "getNewBlindedBlock", + "operationId" : "produceBlindedBlock", "summary" : "Produce unsigned blinded block", "description" : "Requests a beacon node to produce a valid blinded block, which can then be signed by a validator. A blinded block is a block with only a transactions root, rather than a full transactions list.\n\nMetadata in the response indicates the type of block produced, and the supported types of block will be added to as forks progress.\n\nPre-Bellatrix, this endpoint will return a `BeaconBlock`.", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_contribution_and_proofs.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_contribution_and_proofs.json index 3140dce9184..b073abc9e68 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_contribution_and_proofs.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_contribution_and_proofs.json @@ -1,8 +1,8 @@ { "post" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "postContributionAndProofs", - "summary" : "Publish contribution and proofs", + "operationId" : "publishContributionAndProofs", + "summary" : "Publish multiple contribution and proofs", "description" : "Verifies given sync committee contribution and proofs and publishes on appropriate gossipsub topics.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_attester_{epoch}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_attester_{epoch}.json index 21bf342baeb..806517eb7cc 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_attester_{epoch}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_attester_{epoch}.json @@ -1,7 +1,7 @@ { "post" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "postAttesterDuties", + "operationId" : "getAttesterDuties", "summary" : "Get attester duties", "description" : "Requests the beacon node to provide a set of attestation duties, which should be performed by validators, for a particular epoch. Duties should only need to be checked once per epoch, however a chain reorganization (of > MIN_SEED_LOOKAHEAD epochs) could occur, resulting in a change of duties. For full safety, you should monitor head events and confirm the dependent root in this response matches:\n- event.previous_duty_dependent_root when `compute_epoch_at_slot(event.slot) == epoch`\n- event.current_duty_dependent_root when `compute_epoch_at_slot(event.slot) + 1 == epoch`\n- event.block otherwise\n\nThe dependent_root value is `get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch - 1) - 1)` or the genesis block root in the case of underflow.", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_proposer_{epoch}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_proposer_{epoch}.json index 8da5a287b7a..9e7f62c8701 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_proposer_{epoch}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_proposer_{epoch}.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Validator", "Validator Required Api" ], "operationId" : "getProposerDuties", - "summary" : "Get proposer duties", + "summary" : "Get block proposers duties", "description" : "Request beacon node to provide all validators that are scheduled to propose a block in the given epoch.\n\nDuties should only need to be checked once per epoch, however a chain reorganization could occur that results in a change of duties. For full safety, you should monitor head events and confirm the dependent root in this response matches:\n- event.current_duty_dependent_root when `compute_epoch_at_slot(event.slot) == epoch`\n- event.block otherwise\n\nThe dependent_root value is `get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch) - 1)` or the genesis block root in the case of underflow.", "parameters" : [ { "name" : "epoch", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_sync_{epoch}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_sync_{epoch}.json index 167e36a0c2b..4161fd54948 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_sync_{epoch}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_duties_sync_{epoch}.json @@ -1,7 +1,7 @@ { "post" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "postSyncDuties", + "operationId" : "getSyncCommitteeDuties", "summary" : "Get sync committee duties", "description" : "Requests the beacon node to provide a set of sync committee duties", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_liveness_{epoch}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_liveness_{epoch}.json index 1ad379461d0..10da5efaf76 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_liveness_{epoch}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_liveness_{epoch}.json @@ -1,8 +1,8 @@ { "post" : { "tags" : [ "Validator" ], - "operationId" : "postValidatorLiveness", - "summary" : "Get Validator Liveness", + "operationId" : "getLiveness", + "summary" : "Indicates if a validator has been observed on the network", "description" : "Requests the beacon node to indicate if a validator has been observed to be live in a given epoch. The beacon node might detect liveness by observing messages from the validator on the network, in the beacon chain, from its API or from any other source. It is important to note that the values returned by the beacon node are not canonical; they are best-effort and based upon a subjective view of the network.", "parameters" : [ { "name" : "epoch", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_prepare_beacon_proposer.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_prepare_beacon_proposer.json index 99a13e0b742..9b9fa00701e 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_prepare_beacon_proposer.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_prepare_beacon_proposer.json @@ -2,7 +2,7 @@ "post" : { "tags" : [ "Validator", "Validator Required Api" ], "operationId" : "prepareBeaconProposer", - "summary" : "Prepare Beacon Proposers", + "summary" : "Provide beacon node with proposals for the given validators.", "description" : "Prepares the beacon node for potential proposers by supplying information required when proposing blocks for the given validators. The information supplied for each validator index is considered persistent until overwritten by new information for the given validator index, or until the beacon node restarts.\n\nNote that because the information is not persistent across beacon node restarts it is recommended that either the beacon node is monitored for restarts or this information is refreshed by resending this request periodically (for example, each epoch).\n\nAlso note that requests containing currently inactive or unknown validator indices will be accepted, as they may become active at a later epoch.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_register_validator.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_register_validator.json index 6cbb8e11492..43979c5c5b1 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_register_validator.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_register_validator.json @@ -1,8 +1,8 @@ { "post" : { - "tags" : [ "Validator", "Validator Required Api" ], + "tags" : [ "Validator" ], "operationId" : "registerValidator", - "summary" : "Register validators with builder", + "summary" : "Provide beacon node with registrations for the given validators to the external builder network.", "description" : "Prepares the beacon node for engaging with external builders. The information must be sent by the beacon node to the builder network. It is expected that the validator client will send this information periodically to ensure the beacon node has correct and timely registration information to provide to builders. The validator client should not sign blinded beacon blocks that do not adhere to their latest fee recipient and gas limit preferences.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_contribution.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_contribution.json index f800b0ca685..b3aeffdbe8b 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_contribution.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_contribution.json @@ -1,7 +1,7 @@ { "get" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "getSyncCommitteeContribution", + "operationId" : "produceSyncCommitteeContribution", "summary" : "Produce a sync committee contribution", "description" : "Returns a `SyncCommitteeContribution` that is the aggregate of `SyncCommitteeMessage` values known to this node matching the specified slot, subcommittee index and beacon block root.", "parameters" : [ { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_subscriptions.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_subscriptions.json index 22133774bd0..81c6bff367b 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_subscriptions.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v1_validator_sync_committee_subscriptions.json @@ -1,8 +1,8 @@ { "post" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "postSyncCommitteeSubscriptions", - "summary" : "Subscribe to a Sync committee subnet", + "operationId" : "prepareSyncCommitteeSubnets", + "summary" : "Subscribe to sync committee subnets", "description" : "Subscribe to a number of sync committee subnets\n\nSync committees are not present in phase0, but are required for Altair networks.\n\nSubscribing to sync committee subnets is an action performed by VC to enable network participation in Altair networks, and only required if the VC has an active validator in an active sync committee.", "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blinded_blocks.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blinded_blocks.json index 269d3764d46..f0a98e76d1f 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blinded_blocks.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blinded_blocks.json @@ -13,6 +13,15 @@ "example" : "consensus_and_equivocation", "format" : "string" } + }, { + "name" : "Eth-Consensus-Version", + "required" : true, + "in" : "header", + "schema" : { + "type" : "string", + "enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ], + "description" : "Version of the block being submitted." + } } ], "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blocks.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blocks.json index b27886231de..c0e383d8aa1 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blocks.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_beacon_blocks.json @@ -13,6 +13,15 @@ "example" : "consensus_and_equivocation", "format" : "string" } + }, { + "name" : "Eth-Consensus-Version", + "required" : true, + "in" : "header", + "schema" : { + "type" : "string", + "enum" : [ "phase0", "altair", "bellatrix", "capella", "deneb", "electra" ], + "description" : "Version of the block being submitted." + } } ], "requestBody" : { "content" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_validator_blocks_{slot}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_validator_blocks_{slot}.json index 835b73312f4..908688b48d8 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_validator_blocks_{slot}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v2_validator_blocks_{slot}.json @@ -1,8 +1,8 @@ { "get" : { "tags" : [ "Validator", "Validator Required Api" ], - "operationId" : "getNewBlock", - "summary" : "Produce unsigned block", + "operationId" : "produceBlockV2", + "summary" : "Produce a new block, without signature.", "description" : "Requests a beacon node to produce a valid block, which can then be signed by a validator.\nMetadata in the response indicates the type of block produced, and the supported types of block will be added to as forks progress.", "parameters" : [ { "name" : "slot", diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v3_validator_blocks_{slot}.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v3_validator_blocks_{slot}.json index 8d33030722a..0ee02a55cc5 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v3_validator_blocks_{slot}.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/paths/_eth_v3_validator_blocks_{slot}.json @@ -2,7 +2,7 @@ "get" : { "tags" : [ "Validator", "Validator Required Api" ], "operationId" : "produceBlockV3", - "summary" : "Produce a new block, without signature", + "summary" : "Produce a new block, without signature.", "description" : "Requests a beacon node to produce a valid block, which can then be signed by a validator. The\nreturned block may be blinded or unblinded, depending on the current state of the network as\ndecided by the execution and beacon nodes.\nThe beacon node must return an unblinded block if it obtains the execution payload from its\npaired execution node. It must only return a blinded block if it obtains the execution payload\nheader from an MEV relay.\nMetadata in the response indicates the type of block produced, and the supported types of block\nwill be added to as forks progress.", "parameters" : [ { "name" : "slot", diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/BeaconRestApiTypes.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/BeaconRestApiTypes.java index 895a192a04d..400cd536a8a 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/BeaconRestApiTypes.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/BeaconRestApiTypes.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.beaconrestapi; +import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.MILESTONE_TYPE; import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.SIGNATURE_TYPE; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.ATTESTATION_DATA_ROOT; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.BEACON_BLOCK_ROOT; @@ -24,6 +25,7 @@ import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EPOCH; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EPOCH_QUERY_DESCRIPTION; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.GRAFFITI; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.INDEX; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.PARAM_BLOCK_ID; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.PARAM_BLOCK_ID_DESCRIPTION; @@ -71,6 +73,7 @@ import tech.pegasys.teku.infrastructure.json.types.StringValueTypeDefinition; import tech.pegasys.teku.infrastructure.restapi.endpoints.ParameterMetadata; import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlockHeader; import tech.pegasys.teku.spec.datastructures.metadata.BlockAndMetaData; import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel; @@ -241,6 +244,9 @@ public class BeaconRestApiTypes { PARAMETER_BROADCAST_VALIDATION = new ParameterMetadata<>(PARAM_BROADCAST_VALIDATION, BROADCAST_VALIDATION_VALUE); + public static final ParameterMetadata ETH_CONSENSUS_VERSION_TYPE = + new ParameterMetadata<>(HEADER_CONSENSUS_VERSION, MILESTONE_TYPE); + @SuppressWarnings("JavaCase") public enum BroadcastValidationParameter { gossip, diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java index 480ee9fec01..33c8b30ce97 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/JsonTypeDefinitionBeaconRestApi.java @@ -29,7 +29,6 @@ import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.admin.PutLogLevel; import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.admin.Readiness; import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon.GetAllBlocksAtSlot; -import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon.GetDepositSnapshot; import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon.GetDeposits; import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon.GetEth1Data; import tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon.GetEth1DataCache; @@ -46,6 +45,7 @@ import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetBlockHeader; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetBlockHeaders; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetBlockRoot; +import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetDepositSnapshot; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetFinalizedBlockRoot; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetFinalizedCheckpointState; import tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.GetGenesis; @@ -240,6 +240,7 @@ private static RestApi create( .endpoint(new PostVoluntaryExit(dataProvider)) .endpoint(new PostSyncCommittees(dataProvider)) .endpoint(new PostValidatorLiveness(dataProvider)) + .endpoint(new GetDepositSnapshot(eth1DataProvider)) // Event Handler .endpoint( new GetEvents( @@ -302,7 +303,6 @@ private static RestApi create( .endpoint(new GetEth1Data(dataProvider, eth1DataProvider)) .endpoint(new GetEth1DataCache(eth1DataProvider)) .endpoint(new GetEth1VotingSummary(dataProvider, eth1DataProvider)) - .endpoint(new GetDepositSnapshot(eth1DataProvider)) .endpoint(new GetGlobalValidatorInclusion(dataProvider)) .endpoint(new GetValidatorInclusion(dataProvider)); diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttestations.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttestations.java index 22df1968133..bb5bfd2c2e4 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttestations.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttestations.java @@ -48,8 +48,8 @@ public GetAttestations(final DataProvider dataProvider, Spec spec) { public GetAttestations(final NodeDataProvider nodeDataProvider, final Spec spec) { super( EndpointMetadata.get(ROUTE) - .operationId("getAttestations") - .summary("Get attestations") + .operationId("getPoolAttestations") + .summary("Get Attestations from operations pool") .description( "Retrieves attestations known by the node but not necessarily incorporated into any block.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttesterSlashings.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttesterSlashings.java index c40eb50f534..cc40eff277e 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttesterSlashings.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetAttesterSlashings.java @@ -43,8 +43,8 @@ public GetAttesterSlashings(final DataProvider dataProvider, Spec spec) { GetAttesterSlashings(final NodeDataProvider provider, Spec spec) { super( EndpointMetadata.get(ROUTE) - .operationId("getAttesterSlashings") - .summary("Get Attester Slashings") + .operationId("getPoolAttesterSlashings") + .summary("Get AttesterSlashings from operations pool") .description( "Retrieves attester slashings known by the node but not necessarily incorporated into any block.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetBlsToExecutionChanges.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetBlsToExecutionChanges.java index c00f36961d3..dd862a6dfca 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetBlsToExecutionChanges.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetBlsToExecutionChanges.java @@ -75,7 +75,7 @@ private static EndpointMetadata createEndpointMetadata(final SchemaDefinitionCac .build(); return EndpointMetadata.get(ROUTE) - .operationId("getBlsToExecutionChanges") + .operationId("getPoolBLSToExecutionChanges") .summary("Get SignedBLSToExecutionChange from operations pool") .description( "Retrieves BLS to execution changes known by the node but not necessarily incorporated into any block") diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/tekuv1/beacon/GetDepositSnapshot.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetDepositSnapshot.java similarity index 98% rename from data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/tekuv1/beacon/GetDepositSnapshot.java rename to data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetDepositSnapshot.java index f67370d4705..c8a748392f5 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/tekuv1/beacon/GetDepositSnapshot.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetDepositSnapshot.java @@ -11,7 +11,7 @@ * specific language governing permissions and limitations under the License. */ -package tech.pegasys.teku.beaconrestapi.handlers.tekuv1.beacon; +package tech.pegasys.teku.beaconrestapi.handlers.v1.beacon; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK; diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetProposerSlashings.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetProposerSlashings.java index 057f15a9542..6bd9d8ec722 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetProposerSlashings.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetProposerSlashings.java @@ -50,8 +50,8 @@ public GetProposerSlashings(final DataProvider dataProvider) { GetProposerSlashings(final NodeDataProvider provider) { super( EndpointMetadata.get(ROUTE) - .operationId("getProposerSlashings") - .summary("Get proposer slashings") + .operationId("getPoolProposerSlashings") + .summary("Get ProposerSlashings from operations pool") .description( "Retrieves proposer slashings known by the node but not necessarily incorporated into any block.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateCommittees.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateCommittees.java index 236a6786280..adcbac11a95 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateCommittees.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateCommittees.java @@ -74,8 +74,8 @@ public GetStateCommittees(final DataProvider dataProvider) { GetStateCommittees(final ChainDataProvider chainDataProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("getStateCommittees") - .summary("Get committees at state") + .operationId("getEpochCommittees") + .summary("Get all committees for a state.") .description("Retrieves the committees for the given state.") .pathParam(PARAMETER_STATE_ID) .queryParam(EPOCH_PARAMETER) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateFork.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateFork.java index b8b8ecee088..3ff9bbfb0ac 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateFork.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateFork.java @@ -52,8 +52,8 @@ public GetStateFork(final DataProvider dataProvider) { GetStateFork(final ChainDataProvider chainDataProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("getSateFork") - .summary("Get state fork") + .operationId("getStateFork") + .summary("Get Fork object for requested state") .description("Returns Fork object for state with given 'state_id'.") .tags(TAG_BEACON, TAG_VALIDATOR_REQUIRED) .pathParam(PARAMETER_STATE_ID) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRandao.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRandao.java index 0415533e86d..08f65303b32 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRandao.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRandao.java @@ -69,7 +69,7 @@ private static EndpointMetadata createMetadata() { .build(); return EndpointMetadata.get(ROUTE) .operationId("getStateRandao") - .summary("Get state RANDAO") + .summary("Get the RANDAO mix for some epoch in a specified state.") .description( "Fetch the RANDAO mix for the requested epoch from the state identified by `state_id`.\n\n" + "If an epoch is not specified then the RANDAO mix for the state's current epoch will be returned.\n\n" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRoot.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRoot.java index 3c9703fefe7..9a225284082 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRoot.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateRoot.java @@ -48,7 +48,7 @@ public GetStateRoot(final DataProvider dataProvider) { super( EndpointMetadata.get(ROUTE) .operationId("getStateRoot") - .summary("Get state root") + .summary("Get state SSZ HashTreeRoot") .description( "Calculates HashTreeRoot for state with given 'state_id'. If stateId is root, same value will be returned.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateSyncCommittees.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateSyncCommittees.java index 0e9a9659cb4..cec44e00070 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateSyncCommittees.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateSyncCommittees.java @@ -20,7 +20,6 @@ import static tech.pegasys.teku.infrastructure.http.RestApiConstants.EXECUTION_OPTIMISTIC; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.FINALIZED; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_BEACON; -import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_VALIDATOR_REQUIRED; import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BOOLEAN_TYPE; import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.UINT64_TYPE; import static tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition.listOf; @@ -71,10 +70,10 @@ public GetStateSyncCommittees(final DataProvider dataProvider) { public GetStateSyncCommittees(final ChainDataProvider chainDataProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("getStateSyncCommittees") - .summary("Get sync committees") + .operationId("getEpochSyncCommittees") + .summary("Get sync committees for a state.") .description("Retrieves the sync committees for the given state.") - .tags(TAG_BEACON, TAG_VALIDATOR_REQUIRED) + .tags(TAG_BEACON) .pathParam(PARAMETER_STATE_ID) .queryParam(EPOCH_PARAMETER) .response(HttpStatusCodes.SC_OK, "Request successful", RESPONSE_TYPE) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateValidator.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateValidator.java index 9afcbad1472..1f5307c6686 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateValidator.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetStateValidator.java @@ -60,7 +60,7 @@ public GetStateValidator(final DataProvider dataProvider) { super( EndpointMetadata.get(ROUTE) .operationId("getStateValidator") - .summary("Get validator from state") + .summary("Get validator from state by id") .description("Retrieves data about the given peer.") .pathParam(PARAMETER_STATE_ID) .pathParam(PARAMETER_VALIDATOR_ID) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetVoluntaryExits.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetVoluntaryExits.java index 2db36f85c53..69008e2eabc 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetVoluntaryExits.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/GetVoluntaryExits.java @@ -51,8 +51,8 @@ public GetVoluntaryExits(final DataProvider dataProvider) { GetVoluntaryExits(final NodeDataProvider provider) { super( EndpointMetadata.get(ROUTE) - .operationId("getVoluntaryExits") - .summary("Get signed voluntary exits") + .operationId("getPoolVoluntaryExits") + .summary("Get SignedVoluntaryExit from operations pool") .description( "Retrieves voluntary exits known by the node but not necessarily incorporated into any block.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttestation.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttestation.java index 0d6f62a8c17..1aef8da8d4e 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttestation.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttestation.java @@ -48,8 +48,8 @@ public PostAttestation( final ValidatorDataProvider provider, final SchemaDefinitionCache schemaDefinitionCache) { super( EndpointMetadata.post(ROUTE) - .operationId("postAttestation") - .summary("Submit signed attestations") + .operationId("submitPoolAttestations") + .summary("Submit Attestation objects to node") .description( "Submit signed attestations to the beacon node to be validated and submitted if valid.\n\n" + "This endpoint does not protected against slashing.") diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttesterSlashing.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttesterSlashing.java index 24e49e9b1b8..d66aff7b692 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttesterSlashing.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostAttesterSlashing.java @@ -44,8 +44,8 @@ public PostAttesterSlashing(final DataProvider dataProvider, final Spec spec) { public PostAttesterSlashing(final NodeDataProvider provider, final Spec spec) { super( EndpointMetadata.post(ROUTE) - .operationId("postAttesterSlashing") - .summary("Submit attester slashing object") + .operationId("submitPoolAttesterSlashings") + .summary("Submit AttesterSlashing object to node's pool") .description( "Submits attester slashing object to node's pool and if passes validation node MUST broadcast it to network.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlindedBlock.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlindedBlock.java index 1c65d03856b..54b5636d1bb 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlindedBlock.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlindedBlock.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.beaconrestapi.handlers.v1.beacon; +import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.ETH_CONSENSUS_VERSION_TYPE; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.getSchemaDefinitionForAllSupportedMilestones; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.slotBasedSelector; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_ACCEPTED; @@ -95,6 +96,9 @@ private static EndpointMetadata createMetadata( schemaDefinitionCache, SchemaDefinitions::getSignedBlindedBlockContainerSchema), spec::deserializeSignedBlindedBlockContainer) + .header( + ETH_CONSENSUS_VERSION_TYPE.withDescription( + "Version of the block being submitted, if using SSZ encoding.")) .response(SC_OK, "Block has been successfully broadcast, validated and imported.") .response( SC_ACCEPTED, diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlock.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlock.java index 90daf736ba8..d59b01f4590 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlock.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlock.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.beaconrestapi.handlers.v1.beacon; +import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.ETH_CONSENSUS_VERSION_TYPE; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.getSchemaDefinitionForAllSupportedMilestones; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.slotBasedSelector; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_ACCEPTED; @@ -98,6 +99,9 @@ private static EndpointMetadata createMetadata( schemaDefinitionCache, SchemaDefinitions::getSignedBlockContainerSchema), spec::deserializeSignedBlockContainer) + .header( + ETH_CONSENSUS_VERSION_TYPE.withDescription( + "Version of the block being submitted, if using SSZ encoding.")) .response(SC_OK, "Block has been successfully broadcast, validated and imported.") .response( SC_ACCEPTED, diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlsToExecutionChanges.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlsToExecutionChanges.java index 12c17034911..e4c7cacd153 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlsToExecutionChanges.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostBlsToExecutionChanges.java @@ -52,7 +52,7 @@ public PostBlsToExecutionChanges( private static EndpointMetadata createEndpointMetadata(final SchemaDefinitionCache schemaCache) { return EndpointMetadata.post(ROUTE) - .operationId("postBlsToExecutionChange") + .operationId("submitPoolBLSToExecutionChange") .summary("Submit SignedBLSToExecutionChange object to node's pool") .description( "Submits SignedBLSToExecutionChange object to node's pool and if passes validation node MUST broadcast it" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostProposerSlashing.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostProposerSlashing.java index f5256bf13ff..6df0827edee 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostProposerSlashing.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostProposerSlashing.java @@ -41,8 +41,8 @@ public PostProposerSlashing(final DataProvider dataProvider) { public PostProposerSlashing(final NodeDataProvider provider) { super( EndpointMetadata.post(ROUTE) - .operationId("postProposerSlashing") - .summary("Submit proposer slashing object") + .operationId("submitPoolProposerSlashings") + .summary("Submit ProposerSlashing object to node's pool") .description( "Submits proposer slashing object to node's pool and, if it passes validation, the node MUST broadcast it to network.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostSyncCommittees.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostSyncCommittees.java index 3017a95c8ba..f82c3bf23c2 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostSyncCommittees.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostSyncCommittees.java @@ -49,8 +49,8 @@ public PostSyncCommittees(final DataProvider provider) { public PostSyncCommittees(final ValidatorDataProvider provider) { super( EndpointMetadata.post(ROUTE) - .operationId("postSyncCommittees") - .summary("Submit sync committee messages to node") + .operationId("submitPoolSyncCommitteeSignatures") + .summary("Submit sync committee signatures to node") .description( "Submits sync committee message objects to the node.\n\n" + "Sync committee messages are not present in phase0, but are required for Altair networks.\n\n" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostVoluntaryExit.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostVoluntaryExit.java index 5bfb504ce3c..3016aab587a 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostVoluntaryExit.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/beacon/PostVoluntaryExit.java @@ -43,8 +43,8 @@ public PostVoluntaryExit(final DataProvider dataProvider) { public PostVoluntaryExit(final NodeDataProvider provider) { super( EndpointMetadata.post(ROUTE) - .operationId("postVoluntaryExit") - .summary("Submit signed voluntary exit") + .operationId("submitPoolVoluntaryExit") + .summary("Submit SignedVoluntaryExit object to node's pool") .description( "Submits signed voluntary exit object to node's pool and if it passes validation node MUST broadcast it to network.") .tags(TAG_BEACON) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/builder/GetExpectedWithdrawals.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/builder/GetExpectedWithdrawals.java index 8fe565d8d52..11f5833c7e4 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/builder/GetExpectedWithdrawals.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/builder/GetExpectedWithdrawals.java @@ -62,8 +62,9 @@ protected GetExpectedWithdrawals( final SchemaDefinitionCache schemaDefinitionCache) { super( EndpointMetadata.get(ROUTE) - .operationId("GetExpectedWithdrawals") - .summary("Get Expected Withdrawals") + .operationId("getNextWithdrawals") + .summary( + "Get the withdrawals that are to be included for the block built on the specified state.") .description( "Get the withdrawals computed from the specified state, that will be included in the block \n" + " that gets built on the specified state.") diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetDepositContract.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetDepositContract.java index baa270a2f82..60c3a358399 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetDepositContract.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetDepositContract.java @@ -53,7 +53,7 @@ public GetDepositContract( final Eth1Address depositContractAddress, final ConfigProvider configProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("getDepositContractAddress") + .operationId("getDepositContract") .summary("Get deposit contract address") .description("Retrieve deposit contract address and genesis fork version.") .tags(TAG_CONFIG) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetForkSchedule.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetForkSchedule.java index e7c4641b3b8..c10498c4474 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetForkSchedule.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetForkSchedule.java @@ -46,8 +46,8 @@ public GetForkSchedule(final DataProvider dataProvider) { GetForkSchedule(final ConfigProvider configProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("getScheduledForks") - .summary("Get scheduled forks") + .operationId("getForkSchedule") + .summary("Get scheduled upcoming forks.") .description("Retrieve all scheduled upcoming forks this node is aware of.") .tags(TAG_CONFIG) .response(SC_OK, "Success", FORK_SCHEDULE_RESPONSE_TYPE) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetSpec.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetSpec.java index b60dd0fb41f..1694f283da0 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetSpec.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/config/GetSpec.java @@ -42,7 +42,7 @@ public GetSpec(final DataProvider dataProvider) { super( EndpointMetadata.get(ROUTE) .operationId("getSpec") - .summary("Get spec params") + .summary("Get spec params.") .description("Retrieve specification configuration used on this node.") .tags(TAG_CONFIG, TAG_VALIDATOR_REQUIRED) .response(SC_OK, "Success", GET_SPEC_RESPONSE_TYPE) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetHealth.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetHealth.java index 9a437a3245a..dae10871222 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetHealth.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetHealth.java @@ -44,8 +44,8 @@ public GetHealth(final DataProvider provider) { GetHealth(final SyncDataProvider syncProvider, final ChainDataProvider chainDataProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("GetNodeHealth") - .summary("Get node health") + .operationId("getHealth") + .summary("Get health check") .description( "Returns node health status in http status codes. Useful for load balancers.") .queryParam(SYNCING_PARAMETER) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeerById.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeerById.java index 8b5612d8bf2..680b721fa30 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeerById.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeerById.java @@ -50,7 +50,7 @@ public GetPeerById(final DataProvider provider) { GetPeerById(final NetworkDataProvider network) { super( EndpointMetadata.get(ROUTE) - .operationId("getNodePeer") + .operationId("getPeer") .summary("Get node peer") .description("Retrieves data about the given peer.") .pathParam(PEER_ID_PARAMETER) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeers.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeers.java index 877d20f6ec2..1099366db4a 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeers.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeers.java @@ -107,8 +107,8 @@ public GetPeers(final DataProvider provider) { GetPeers(final NetworkDataProvider network) { super( EndpointMetadata.get(ROUTE) - .operationId("getNodePeers") - .summary("Get node peers") + .operationId("getPeers") + .summary("Get node network peers") .description("Retrieves data about the node's network peers.") .tags(TAG_NODE) .response(SC_OK, "Request successful", PEERS_RESPONSE_TYPE) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetSyncing.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetSyncing.java index 4b6c25e19f2..6792868a46f 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetSyncing.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetSyncing.java @@ -67,7 +67,7 @@ public GetSyncing(final DataProvider provider) { final ExecutionClientDataProvider executionClientDataProvider) { super( EndpointMetadata.get(ROUTE) - .operationId("getNodeSyncingStatus") + .operationId("getSyncingStatus") .summary("Get node syncing status") .description( "Requests the beacon node to describe if it's currently syncing or not, " diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetVersion.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetVersion.java index 7e075351db0..5aa2c65ca20 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetVersion.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetVersion.java @@ -45,7 +45,7 @@ public GetVersion() { super( EndpointMetadata.get(ROUTE) .operationId("getNodeVersion") - .summary("Get node version") + .summary("Get version string of the running beacon node.") .description( "similar to [HTTP User-Agent](https://tools.ietf.org/html/rfc7231#section-5.5.3).") .tags(TAG_NODE) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetAttestationRewards.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetAttestationRewards.java index f667d56a71e..b809f0d9128 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetAttestationRewards.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetAttestationRewards.java @@ -95,7 +95,7 @@ public GetAttestationRewards(final ChainDataProvider chainDataProvider) { super( EndpointMetadata.post(ROUTE) .operationId("getAttestationsRewards") - .summary("Get Attestations Rewards") + .summary("Get attestations rewards") .description( "Retrieve attestation reward info for validators specified by array of public keys or validator index" + ". If no array is provided, return reward info for every validator.") diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetBlockRewards.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetBlockRewards.java index 17a99df8dcc..415a13c8223 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetBlockRewards.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetBlockRewards.java @@ -66,7 +66,7 @@ public GetBlockRewards(final ChainDataProvider chainDataProvider) { super( EndpointMetadata.get(ROUTE) .operationId("getBlockRewards") - .summary("Get Block Rewards") + .summary("Get block rewards") .description("Retrieve block reward info for a single block.") .tags(TAG_BEACON, TAG_REWARDS) .pathParam(PARAMETER_BLOCK_ID) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetSyncCommitteeRewards.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetSyncCommitteeRewards.java index 991b4a9b28d..c6a5ac27577 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetSyncCommitteeRewards.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/rewards/GetSyncCommitteeRewards.java @@ -75,7 +75,7 @@ public GetSyncCommitteeRewards(final ChainDataProvider chainDataProvider) { super( EndpointMetadata.post(ROUTE) .operationId("getSyncCommitteeRewards") - .summary("Get Sync Committee Rewards") + .summary("Get sync committee rewards") .description( "Retrieves rewards info for sync committee members specified by array of public keys " + "or validator index. If no array is provided, return reward info for every committee member.") diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAggregateAttestation.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAggregateAttestation.java index d15fc8df174..4c7c850f41e 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAggregateAttestation.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAggregateAttestation.java @@ -52,8 +52,8 @@ public GetAggregateAttestation(final DataProvider dataProvider, final Spec spec) public GetAggregateAttestation(final ValidatorDataProvider provider, final Spec spec) { super( EndpointMetadata.get(ROUTE) - .operationId("getAggregateAttestation") - .summary("Get aggregated attestations") + .operationId("getAggregatedAttestation") + .summary("Get aggregated attestation") .description( "Aggregates all attestations matching given attestation data root and slot.") .tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAttestationData.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAttestationData.java index 595617a6467..9ea02ad9c3e 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAttestationData.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetAttestationData.java @@ -58,7 +58,7 @@ public GetAttestationData(final DataProvider provider) { public GetAttestationData(final ValidatorDataProvider provider) { super( EndpointMetadata.get(ROUTE) - .operationId("getAttestationData") + .operationId("produceAttestationData") .summary("Produce an AttestationData") .description("Requests that the beacon node produce an AttestationData.") .tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetNewBlindedBlock.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetNewBlindedBlock.java index a3ce94c5b5d..cda922692ec 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetNewBlindedBlock.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetNewBlindedBlock.java @@ -68,7 +68,7 @@ public GetNewBlindedBlock( private static EndpointMetadata getEndpointMetaData( final Spec spec, final SchemaDefinitionCache schemaDefinitionCache) { return EndpointMetadata.get(ROUTE) - .operationId("getNewBlindedBlock") + .operationId("produceBlindedBlock") .summary("Produce unsigned blinded block") .description( "Requests a beacon node to produce a valid blinded block, which can then be signed by a validator. " diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetProposerDuties.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetProposerDuties.java index 6ef14703b80..4f2ffd2c7eb 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetProposerDuties.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetProposerDuties.java @@ -50,7 +50,7 @@ public GetProposerDuties(final DataProvider dataProvider) { super( EndpointMetadata.get(ROUTE) .operationId("getProposerDuties") - .summary("Get proposer duties") + .summary("Get block proposers duties") .description( "Request beacon node to provide all validators that are scheduled to propose a block in the given epoch.\n\n" + "Duties should only need to be checked once per epoch, " diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetSyncCommitteeContribution.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetSyncCommitteeContribution.java index 216fb413f87..86caf66cf8e 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetSyncCommitteeContribution.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/GetSyncCommitteeContribution.java @@ -55,7 +55,7 @@ public GetSyncCommitteeContribution( final SchemaDefinitionCache schemaDefinitionCache) { super( EndpointMetadata.get(ROUTE) - .operationId("getSyncCommitteeContribution") + .operationId("produceSyncCommitteeContribution") .summary("Produce a sync committee contribution") .description( "Returns a `SyncCommitteeContribution` that is the aggregate of `SyncCommitteeMessage` " diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAggregateAndProofs.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAggregateAndProofs.java index 4f92a4f2dac..fa020b9c1bd 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAggregateAndProofs.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAggregateAndProofs.java @@ -46,8 +46,8 @@ public PostAggregateAndProofs( final ValidatorDataProvider provider, final SchemaDefinitions schemaDefinitions) { super( EndpointMetadata.post(ROUTE) - .operationId("postAggregateAndProofs") - .summary("Publish aggregate and proofs") + .operationId("publishAggregateAndProofs") + .summary("Publish multiple aggregate and proofs") .description( "Verifies given aggregate and proofs and publishes it on appropriate gossipsub topic.") .tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAttesterDuties.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAttesterDuties.java index 11e19d05b79..57e0a084ed4 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAttesterDuties.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostAttesterDuties.java @@ -51,7 +51,7 @@ public PostAttesterDuties(final DataProvider dataProvider) { final SyncDataProvider syncDataProvider, final ValidatorDataProvider validatorDataProvider) { super( EndpointMetadata.post(ROUTE) - .operationId("postAttesterDuties") + .operationId("getAttesterDuties") .summary("Get attester duties") .description( "Requests the beacon node to provide a set of attestation duties, " diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostContributionAndProofs.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostContributionAndProofs.java index 57b9ff60a0e..a818f3ac1ab 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostContributionAndProofs.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostContributionAndProofs.java @@ -45,8 +45,8 @@ public PostContributionAndProofs( final ValidatorDataProvider provider, final SchemaDefinitionCache schemaDefinitionCache) { super( EndpointMetadata.post(ROUTE) - .operationId("postContributionAndProofs") - .summary("Publish contribution and proofs") + .operationId("publishContributionAndProofs") + .summary("Publish multiple contribution and proofs") .description( "Verifies given sync committee contribution and proofs and publishes on appropriate gossipsub topics.") .tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostPrepareBeaconProposer.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostPrepareBeaconProposer.java index 2f53a77dce2..e20e2adfbf7 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostPrepareBeaconProposer.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostPrepareBeaconProposer.java @@ -91,7 +91,7 @@ public void handleRequest(final RestApiRequest request) throws JsonProcessingExc private static EndpointMetadata createMetadata() { return EndpointMetadata.post(ROUTE) .operationId("prepareBeaconProposer") - .summary("Prepare Beacon Proposers") + .summary("Provide beacon node with proposals for the given validators.") .description( "Prepares the beacon node for potential proposers by supplying information required when proposing blocks for the given validators. The information supplied for each validator index is considered persistent until overwritten by new information for the given validator index, or until the beacon node restarts.\n\n" + "Note that because the information is not persistent across beacon node restarts it is recommended that either the beacon node is monitored for restarts or this information is refreshed by resending this request periodically (for example, each epoch).\n\n" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostRegisterValidator.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostRegisterValidator.java index 42940ef13c9..5072add41dc 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostRegisterValidator.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostRegisterValidator.java @@ -16,7 +16,6 @@ import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_INTERNAL_SERVER_ERROR; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK; import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_VALIDATOR; -import static tech.pegasys.teku.infrastructure.http.RestApiConstants.TAG_VALIDATOR_REQUIRED; import static tech.pegasys.teku.spec.schemas.ApiSchemas.SIGNED_VALIDATOR_REGISTRATIONS_SCHEMA; import com.fasterxml.jackson.core.JsonProcessingException; @@ -41,7 +40,8 @@ public PostRegisterValidator(final ValidatorDataProvider validatorDataProvider) super( EndpointMetadata.post(ROUTE) .operationId("registerValidator") - .summary("Register validators with builder") + .summary( + "Provide beacon node with registrations for the given validators to the external builder network.") .description( "Prepares the beacon node for engaging with external builders." + " The information must be sent by the beacon node to the builder network." @@ -49,7 +49,7 @@ public PostRegisterValidator(final ValidatorDataProvider validatorDataProvider) + " to ensure the beacon node has correct and timely registration information" + " to provide to builders. The validator client should not sign blinded beacon" + " blocks that do not adhere to their latest fee recipient and gas limit preferences.") - .tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED) + .tags(TAG_VALIDATOR) .requestBodyType( SIGNED_VALIDATOR_REGISTRATIONS_SCHEMA.getJsonTypeDefinition(), SIGNED_VALIDATOR_REGISTRATIONS_SCHEMA::sszDeserialize) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSubscribeToBeaconCommitteeSubnet.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSubscribeToBeaconCommitteeSubnet.java index 08a0d2951e5..73e43a2bc7e 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSubscribeToBeaconCommitteeSubnet.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSubscribeToBeaconCommitteeSubnet.java @@ -76,8 +76,8 @@ public PostSubscribeToBeaconCommitteeSubnet(final DataProvider dataProvider) { public PostSubscribeToBeaconCommitteeSubnet(final ValidatorDataProvider provider) { super( EndpointMetadata.post(ROUTE) - .operationId("postSubscribeToBeaconCommitteeSubnet") - .summary("Subscribe to a committee subnet") + .operationId("prepareBeaconCommitteeSubnet") + .summary("Signal beacon node to prepare for a committee subnet") .description( "After Beacon node receives this request, search using discv5 for peers related to this subnet and replace current peers with those ones if necessary If validator is_aggregator, beacon node must:\n" + "- announce subnet topic subscription on gossipsub\n" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncCommitteeSubscriptions.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncCommitteeSubscriptions.java index 117f91362ad..b5444f99504 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncCommitteeSubscriptions.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncCommitteeSubscriptions.java @@ -67,8 +67,8 @@ public PostSyncCommitteeSubscriptions(final DataProvider dataProvider) { public PostSyncCommitteeSubscriptions(final ValidatorDataProvider validatorDataProvider) { super( EndpointMetadata.post(ROUTE) - .operationId("postSyncCommitteeSubscriptions") - .summary("Subscribe to a Sync committee subnet") + .operationId("prepareSyncCommitteeSubnets") + .summary("Subscribe to sync committee subnets") .description( "Subscribe to a number of sync committee subnets\n\n" + "Sync committees are not present in phase0, but are required for Altair networks.\n\n" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncDuties.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncDuties.java index 0838e22ebfa..f4359020ca9 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncDuties.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostSyncDuties.java @@ -52,7 +52,7 @@ public PostSyncDuties(final DataProvider dataProvider) { final SyncDataProvider syncDataProvider, final ValidatorDataProvider validatorDataProvider) { super( EndpointMetadata.post(ROUTE) - .operationId("postSyncDuties") + .operationId("getSyncCommitteeDuties") .summary("Get sync committee duties") .description("Requests the beacon node to provide a set of sync committee duties") .tags(TAG_VALIDATOR, TAG_VALIDATOR_REQUIRED) diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostValidatorLiveness.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostValidatorLiveness.java index 9513912fdba..d23b315d1c8 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostValidatorLiveness.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/validator/PostValidatorLiveness.java @@ -65,8 +65,8 @@ public PostValidatorLiveness( final SyncDataProvider syncDataProvider) { super( EndpointMetadata.post(ROUTE) - .operationId("postValidatorLiveness") - .summary("Get Validator Liveness") + .operationId("getLiveness") + .summary("Indicates if a validator has been observed on the network") .description( "Requests the beacon node to indicate if a validator has been" + " observed to be live in a given epoch. The beacon node might detect liveness by" diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlindedBlockV2.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlindedBlockV2.java index 87c3d788136..5fe6da80e54 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlindedBlockV2.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlindedBlockV2.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.beaconrestapi.handlers.v2.beacon; +import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.ETH_CONSENSUS_VERSION_TYPE; import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.PARAMETER_BROADCAST_VALIDATION; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.getSchemaDefinitionForAllSupportedMilestones; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.slotBasedSelector; @@ -117,6 +118,8 @@ broadcast but a different status code is returned (202). Pre-Bellatrix, this end schemaDefinitionCache, SchemaDefinitions::getSignedBlindedBlockContainerSchema), spec::deserializeSignedBlindedBlockContainer) + .headerRequired( + ETH_CONSENSUS_VERSION_TYPE.withDescription("Version of the block being submitted.")) .response(SC_OK, "Block has been successfully broadcast, validated and imported.") .response( SC_ACCEPTED, diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlockV2.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlockV2.java index 4e55bac9921..78200d40670 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlockV2.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/beacon/PostBlockV2.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.beaconrestapi.handlers.v2.beacon; +import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.ETH_CONSENSUS_VERSION_TYPE; import static tech.pegasys.teku.beaconrestapi.BeaconRestApiTypes.PARAMETER_BROADCAST_VALIDATION; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.getSchemaDefinitionForAllSupportedMilestones; import static tech.pegasys.teku.beaconrestapi.handlers.v1.beacon.MilestoneDependentTypesUtil.slotBasedSelector; @@ -117,6 +118,8 @@ validation, a separate success response code (202) is used to indicate that the schemaDefinitionCache, SchemaDefinitions::getSignedBlockContainerSchema), spec::deserializeSignedBlockContainer) + .headerRequired( + ETH_CONSENSUS_VERSION_TYPE.withDescription("Version of the block being submitted.")) .response(SC_OK, "Block has been successfully broadcast, validated and imported.") .response( SC_ACCEPTED, diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/GetNewBlock.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/GetNewBlock.java index 636f71261b2..c737a93eca2 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/GetNewBlock.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v2/validator/GetNewBlock.java @@ -68,8 +68,8 @@ public GetNewBlock( private static EndpointMetadata getEndpointMetaData( final Spec spec, final SchemaDefinitionCache schemaDefinitionCache) { return EndpointMetadata.get(ROUTE) - .operationId("getNewBlock") - .summary("Produce unsigned block") + .operationId("produceBlockV2") + .summary("Produce a new block, without signature.") .description( "Requests a beacon node to produce a valid block, which can then be signed by a validator.\n" + "Metadata in the response indicates the type of block produced, and the supported types of block " diff --git a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v3/validator/GetNewBlockV3.java b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v3/validator/GetNewBlockV3.java index 2745e0aab94..9c4ffb2f690 100644 --- a/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v3/validator/GetNewBlockV3.java +++ b/data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v3/validator/GetNewBlockV3.java @@ -81,7 +81,7 @@ private static EndpointMetadata getEndpointMetaData( final SchemaDefinitionCache schemaDefinitionCache) { return EndpointMetadata.get(ROUTE) .operationId("produceBlockV3") - .summary("Produce a new block, without signature") + .summary("Produce a new block, without signature.") .description( "Requests a beacon node to produce a valid block, which can then be signed by a validator. The\n" + "returned block may be blinded or unblinded, depending on the current state of the network as\n" diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ExecutionEngineClient.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ExecutionEngineClient.java index c4e94d8b45e..02ee14282b5 100644 --- a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ExecutionEngineClient.java +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ExecutionEngineClient.java @@ -20,10 +20,12 @@ import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV1; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV2; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV2Response; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV3Response; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3; @@ -47,6 +49,8 @@ public interface ExecutionEngineClient { SafeFuture> getPayloadV3(Bytes8 payloadId); + SafeFuture> getPayloadV4(Bytes8 payloadId); + SafeFuture> newPayloadV1(ExecutionPayloadV1 executionPayload); SafeFuture> newPayloadV2(ExecutionPayloadV2 executionPayload); @@ -56,6 +60,11 @@ SafeFuture> newPayloadV3( List blobVersionedHashes, Bytes32 parentBeaconBlockRoot); + SafeFuture> newPayloadV4( + ExecutionPayloadV4 executionPayload, + List blobVersionedHashes, + Bytes32 parentBeaconBlockRoot); + SafeFuture> forkChoiceUpdatedV1( ForkChoiceStateV1 forkChoiceState, Optional payloadAttributes); diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ThrottlingExecutionEngineClient.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ThrottlingExecutionEngineClient.java index 45bb127707a..964c527cd4b 100644 --- a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ThrottlingExecutionEngineClient.java +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/ThrottlingExecutionEngineClient.java @@ -21,10 +21,12 @@ import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV1; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV2; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV2Response; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV3Response; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3; @@ -79,6 +81,11 @@ public SafeFuture> getPayloadV3(final Bytes8 payl return taskQueue.queueTask(() -> delegate.getPayloadV3(payloadId)); } + @Override + public SafeFuture> getPayloadV4(final Bytes8 payloadId) { + return taskQueue.queueTask(() -> delegate.getPayloadV4(payloadId)); + } + @Override public SafeFuture> newPayloadV1( final ExecutionPayloadV1 executionPayload) { @@ -100,6 +107,15 @@ public SafeFuture> newPayloadV3( () -> delegate.newPayloadV3(executionPayload, blobVersionedHashes, parentBeaconBlockRoot)); } + @Override + public SafeFuture> newPayloadV4( + final ExecutionPayloadV4 executionPayload, + final List blobVersionedHashes, + final Bytes32 parentBeaconBlockRoot) { + return taskQueue.queueTask( + () -> delegate.newPayloadV4(executionPayload, blobVersionedHashes, parentBeaconBlockRoot)); + } + @Override public SafeFuture> forkChoiceUpdatedV1( final ForkChoiceStateV1 forkChoiceState, diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineGetPayloadV4.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineGetPayloadV4.java new file mode 100644 index 00000000000..cedf1fbe2aa --- /dev/null +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineGetPayloadV4.java @@ -0,0 +1,101 @@ +/* + * Copyright Consensys Software Inc., 2023 + * + * 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.executionclient.methods; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import tech.pegasys.teku.ethereum.executionclient.ExecutionEngineClient; +import tech.pegasys.teku.ethereum.executionclient.response.ResponseUnwrapper; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSchema; +import tech.pegasys.teku.spec.datastructures.execution.BlobsBundle; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadContext; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadSchema; +import tech.pegasys.teku.spec.datastructures.execution.GetPayloadResponse; +import tech.pegasys.teku.spec.schemas.SchemaDefinitions; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; + +public class EngineGetPayloadV4 extends AbstractEngineJsonRpcMethod { + + private static final Logger LOG = LogManager.getLogger(); + + private final Spec spec; + + public EngineGetPayloadV4(final ExecutionEngineClient executionEngineClient, final Spec spec) { + super(executionEngineClient); + this.spec = spec; + } + + @Override + public String getName() { + return EngineApiMethod.ENGINE_GET_PAYLOAD.getName(); + } + + @Override + public int getVersion() { + return 4; + } + + @Override + public SafeFuture execute(final JsonRpcRequestParams params) { + final ExecutionPayloadContext executionPayloadContext = + params.getRequiredParameter(0, ExecutionPayloadContext.class); + final UInt64 slot = params.getRequiredParameter(1, UInt64.class); + + LOG.trace( + "Calling {}(payloadId={}, slot={})", + getVersionedName(), + executionPayloadContext.getPayloadId(), + slot); + + return executionEngineClient + .getPayloadV4(executionPayloadContext.getPayloadId()) + .thenApply(ResponseUnwrapper::unwrapExecutionClientResponseOrThrow) + .thenApply( + response -> { + final SchemaDefinitions schemaDefinitions = spec.atSlot(slot).getSchemaDefinitions(); + final ExecutionPayloadSchema payloadSchema = + SchemaDefinitionsBellatrix.required(schemaDefinitions) + .getExecutionPayloadSchema(); + final ExecutionPayload executionPayload = + response.executionPayload.asInternalExecutionPayload(payloadSchema); + final BlobsBundle blobsBundle = getBlobsBundle(response, schemaDefinitions); + return new GetPayloadResponse( + executionPayload, + response.blockValue, + blobsBundle, + response.shouldOverrideBuilder); + }) + .thenPeek( + getPayloadResponse -> + LOG.trace( + "Response {}(payloadId={}, slot={}) -> {}", + getVersionedName(), + executionPayloadContext.getPayloadId(), + slot, + getPayloadResponse)); + } + + private BlobsBundle getBlobsBundle( + final GetPayloadV4Response response, final SchemaDefinitions schemaDefinitions) { + final BlobSchema blobSchema = + SchemaDefinitionsDeneb.required(schemaDefinitions).getBlobSchema(); + return response.blobsBundle.asInternalBlobsBundle(blobSchema); + } +} diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineNewPayloadV4.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineNewPayloadV4.java new file mode 100644 index 00000000000..250ddfcc8f9 --- /dev/null +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineNewPayloadV4.java @@ -0,0 +1,77 @@ +/* + * Copyright Consensys Software Inc., 2023 + * + * 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.executionclient.methods; + +import java.util.List; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.ethereum.executionclient.ExecutionEngineClient; +import tech.pegasys.teku.ethereum.executionclient.response.ResponseUnwrapper; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; +import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.executionlayer.PayloadStatus; +import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; + +public class EngineNewPayloadV4 extends AbstractEngineJsonRpcMethod { + + private static final Logger LOG = LogManager.getLogger(); + + public EngineNewPayloadV4(final ExecutionEngineClient executionEngineClient) { + super(executionEngineClient); + } + + @Override + public String getName() { + return EngineApiMethod.ENGINE_NEW_PAYLOAD.getName(); + } + + @Override + public int getVersion() { + return 4; + } + + @Override + public SafeFuture execute(final JsonRpcRequestParams params) { + final ExecutionPayload executionPayload = + params.getRequiredParameter(0, ExecutionPayload.class); + final List blobVersionedHashes = + params.getRequiredListParameter(1, VersionedHash.class); + final Bytes32 parentBeaconBlockRoot = params.getRequiredParameter(2, Bytes32.class); + + LOG.trace( + "Calling {}(executionPayload={}, blobVersionedHashes={}, parentBeaconBlockRoot={})", + getVersionedName(), + executionPayload, + blobVersionedHashes, + parentBeaconBlockRoot); + + final ExecutionPayloadV4 executionPayloadV4 = + ExecutionPayloadV4.fromInternalExecutionPayload(executionPayload); + return executionEngineClient + .newPayloadV4(executionPayloadV4, blobVersionedHashes, parentBeaconBlockRoot) + .thenApply(ResponseUnwrapper::unwrapExecutionClientResponseOrThrow) + .thenApply(PayloadStatusV1::asInternalExecutionPayload) + .thenPeek( + payloadStatus -> + LOG.trace( + "Response {}(executionPayload={}) -> {}", + getVersionedName(), + executionPayload, + payloadStatus)) + .exceptionally(PayloadStatus::failedExecution); + } +} diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/metrics/MetricRecordingExecutionEngineClient.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/metrics/MetricRecordingExecutionEngineClient.java index 6a4815b58c8..f45056c92a0 100644 --- a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/metrics/MetricRecordingExecutionEngineClient.java +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/metrics/MetricRecordingExecutionEngineClient.java @@ -23,10 +23,12 @@ import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV1; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV2; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV2Response; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV3Response; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3; @@ -59,7 +61,9 @@ public class MetricRecordingExecutionEngineClient extends MetricRecordingAbstrac public static final String FORKCHOICE_UPDATED_WITH_ATTRIBUTES_V3_METHOD = "forkchoice_updated_with_attributesV3"; public static final String GET_PAYLOAD_V3_METHOD = "get_payloadV3"; + public static final String GET_PAYLOAD_V4_METHOD = "get_payloadV4"; public static final String NEW_PAYLOAD_V3_METHOD = "new_payloadV3"; + public static final String NEW_PAYLOAD_V4_METHOD = "new_payloadV4"; public static final String EXCHANGE_CAPABILITIES_METHOD = "exchange_capabilities"; public static final String GET_CLIENT_VERSION_V1_METHOD = "get_client_versionV1"; @@ -106,6 +110,11 @@ public SafeFuture> getPayloadV3(final Bytes8 payl return countRequest(() -> delegate.getPayloadV3(payloadId), GET_PAYLOAD_V3_METHOD); } + @Override + public SafeFuture> getPayloadV4(final Bytes8 payloadId) { + return countRequest(() -> delegate.getPayloadV4(payloadId), GET_PAYLOAD_V4_METHOD); + } + @Override public SafeFuture> newPayloadV1( final ExecutionPayloadV1 executionPayload) { @@ -128,6 +137,16 @@ public SafeFuture> newPayloadV3( NEW_PAYLOAD_V3_METHOD); } + @Override + public SafeFuture> newPayloadV4( + final ExecutionPayloadV4 executionPayload, + final List blobVersionedHashes, + final Bytes32 parentBeaconBlockRoot) { + return countRequest( + () -> delegate.newPayloadV4(executionPayload, blobVersionedHashes, parentBeaconBlockRoot), + NEW_PAYLOAD_V4_METHOD); + } + @Override public SafeFuture> forkChoiceUpdatedV1( final ForkChoiceStateV1 forkChoiceState, diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/DepositReceiptV1.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/DepositReceiptV1.java new file mode 100644 index 00000000000..7d2b70f0ac8 --- /dev/null +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/DepositReceiptV1.java @@ -0,0 +1,63 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.executionclient.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.bytes.Bytes48; +import tech.pegasys.teku.ethereum.executionclient.serialization.Bytes32Deserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.Bytes48Deserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.BytesDeserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.BytesSerializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.UInt64AsHexDeserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.UInt64AsHexSerializer; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public class DepositReceiptV1 { + @JsonSerialize(using = BytesSerializer.class) + @JsonDeserialize(using = Bytes48Deserializer.class) + public final Bytes48 pubkey; + + @JsonSerialize(using = BytesSerializer.class) + @JsonDeserialize(using = Bytes32Deserializer.class) + public final Bytes32 withdrawalCredentials; + + @JsonSerialize(using = UInt64AsHexSerializer.class) + @JsonDeserialize(using = UInt64AsHexDeserializer.class) + public final UInt64 amount; + + @JsonSerialize(using = BytesSerializer.class) + @JsonDeserialize(using = BytesDeserializer.class) + public final Bytes signature; + + @JsonSerialize(using = UInt64AsHexSerializer.class) + @JsonDeserialize(using = UInt64AsHexDeserializer.class) + public final UInt64 index; + + public DepositReceiptV1( + @JsonProperty("pubkey") final Bytes48 pubkey, + @JsonProperty("withdrawalCredentials") final Bytes32 withdrawalCredentials, + @JsonProperty("amount") final UInt64 amount, + @JsonProperty("signature") final Bytes signature, + @JsonProperty("index") final UInt64 index) { + this.pubkey = pubkey; + this.withdrawalCredentials = withdrawalCredentials; + this.amount = amount; + this.signature = signature; + this.index = index; + } +} diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/ExecutionPayloadV4.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/ExecutionPayloadV4.java new file mode 100644 index 00000000000..9b14eb870ab --- /dev/null +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/ExecutionPayloadV4.java @@ -0,0 +1,184 @@ +/* + * Copyright Consensys Software Inc., 2022 + * + * 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.executionclient.schema; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; +import tech.pegasys.teku.bls.BLSPublicKey; +import tech.pegasys.teku.bls.BLSSignature; +import tech.pegasys.teku.infrastructure.bytes.Bytes20; +import tech.pegasys.teku.infrastructure.ssz.SszList; +import tech.pegasys.teku.infrastructure.ssz.collections.impl.SszByteListImpl; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadBuilder; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadSchema; +import tech.pegasys.teku.spec.datastructures.execution.versions.deneb.ExecutionPayloadDeneb; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerExit; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionPayloadElectra; + +public class ExecutionPayloadV4 extends ExecutionPayloadV3 { + public final List depositReceipts; + public final List exits; + + public ExecutionPayloadV4( + @JsonProperty("parentHash") Bytes32 parentHash, + @JsonProperty("feeRecipient") Bytes20 feeRecipient, + @JsonProperty("stateRoot") Bytes32 stateRoot, + @JsonProperty("receiptsRoot") Bytes32 receiptsRoot, + @JsonProperty("logsBloom") Bytes logsBloom, + @JsonProperty("prevRandao") Bytes32 prevRandao, + @JsonProperty("blockNumber") UInt64 blockNumber, + @JsonProperty("gasLimit") UInt64 gasLimit, + @JsonProperty("gasUsed") UInt64 gasUsed, + @JsonProperty("timestamp") UInt64 timestamp, + @JsonProperty("extraData") Bytes extraData, + @JsonProperty("baseFeePerGas") UInt256 baseFeePerGas, + @JsonProperty("blockHash") Bytes32 blockHash, + @JsonProperty("transactions") List transactions, + @JsonProperty("withdrawals") List withdrawals, + @JsonProperty("blobGasUsed") UInt64 blobGasUsed, + @JsonProperty("excessBlobGas") UInt64 excessBlobGas, + @JsonProperty("depositReceipts") List depositReceipts, + @JsonProperty("exits") List exits) { + super( + parentHash, + feeRecipient, + stateRoot, + receiptsRoot, + logsBloom, + prevRandao, + blockNumber, + gasLimit, + gasUsed, + timestamp, + extraData, + baseFeePerGas, + blockHash, + transactions, + withdrawals, + blobGasUsed, + excessBlobGas); + this.depositReceipts = depositReceipts; + this.exits = exits; + } + + public static ExecutionPayloadV4 fromInternalExecutionPayload( + final ExecutionPayload executionPayload) { + final List withdrawalsList = + getWithdrawals(executionPayload.getOptionalWithdrawals()); + return new ExecutionPayloadV4( + executionPayload.getParentHash(), + executionPayload.getFeeRecipient(), + executionPayload.getStateRoot(), + executionPayload.getReceiptsRoot(), + executionPayload.getLogsBloom(), + executionPayload.getPrevRandao(), + executionPayload.getBlockNumber(), + executionPayload.getGasLimit(), + executionPayload.getGasUsed(), + executionPayload.getTimestamp(), + executionPayload.getExtraData(), + executionPayload.getBaseFeePerGas(), + executionPayload.getBlockHash(), + executionPayload.getTransactions().stream().map(SszByteListImpl::getBytes).toList(), + withdrawalsList, + executionPayload.toVersionDeneb().map(ExecutionPayloadDeneb::getBlobGasUsed).orElse(null), + executionPayload.toVersionDeneb().map(ExecutionPayloadDeneb::getExcessBlobGas).orElse(null), + getDepositReceipts( + executionPayload.toVersionElectra().map(ExecutionPayloadElectra::getDepositReceipts)), + getExits(executionPayload.toVersionElectra().map(ExecutionPayloadElectra::getExits))); + } + + @Override + protected ExecutionPayloadBuilder applyToBuilder( + final ExecutionPayloadSchema executionPayloadSchema, + final ExecutionPayloadBuilder builder) { + return super.applyToBuilder(executionPayloadSchema, builder) + .depositReceipts( + () -> + checkNotNull(depositReceipts, "depositReceipts not provided when required").stream() + .map( + depositReceiptV1 -> + createInternalDepositReceipt(depositReceiptV1, executionPayloadSchema)) + .toList()) + .exits( + () -> + checkNotNull(exits, "exits not provided when required").stream() + .map(exitV1 -> createInternalExit(exitV1, executionPayloadSchema)) + .toList()); + } + + private DepositReceipt createInternalDepositReceipt( + final DepositReceiptV1 depositReceiptV1, + final ExecutionPayloadSchema executionPayloadSchema) { + return executionPayloadSchema + .getDepositReceiptSchemaRequired() + .create( + BLSPublicKey.fromBytesCompressed(depositReceiptV1.pubkey), + depositReceiptV1.withdrawalCredentials, + depositReceiptV1.amount, + BLSSignature.fromBytesCompressed(depositReceiptV1.signature), + depositReceiptV1.index); + } + + private ExecutionLayerExit createInternalExit( + final ExitV1 exitV1, final ExecutionPayloadSchema executionPayloadSchema) { + return executionPayloadSchema + .getExecutionLayerExitSchemaRequired() + .create(exitV1.sourceAddress, BLSPublicKey.fromBytesCompressed(exitV1.validatorPublicKey)); + } + + public static List getDepositReceipts( + final Optional> maybeDepositReceipts) { + if (maybeDepositReceipts.isEmpty()) { + return List.of(); + } + + final List depositReceipts = new ArrayList<>(); + + for (DepositReceipt depositReceipt : maybeDepositReceipts.get()) { + depositReceipts.add( + new DepositReceiptV1( + depositReceipt.getPubkey().toBytesCompressed(), + depositReceipt.getWithdrawalCredentials(), + depositReceipt.getAmount(), + depositReceipt.getSignature().toBytesCompressed(), + depositReceipt.getIndex())); + } + return depositReceipts; + } + + public static List getExits(final Optional> maybeExits) { + if (maybeExits.isEmpty()) { + return List.of(); + } + + final List exits = new ArrayList<>(); + + for (ExecutionLayerExit exit : maybeExits.get()) { + exits.add( + new ExitV1(exit.getSourceAddress(), exit.getValidatorPublicKey().toBytesCompressed())); + } + return exits; + } +} diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/ExitV1.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/ExitV1.java new file mode 100644 index 00000000000..aa8bede2a75 --- /dev/null +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/ExitV1.java @@ -0,0 +1,41 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.executionclient.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.apache.tuweni.bytes.Bytes48; +import tech.pegasys.teku.ethereum.executionclient.serialization.Bytes20Deserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.Bytes20Serializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.Bytes48Deserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.BytesSerializer; +import tech.pegasys.teku.infrastructure.bytes.Bytes20; + +public class ExitV1 { + @JsonSerialize(using = Bytes20Serializer.class) + @JsonDeserialize(using = Bytes20Deserializer.class) + public final Bytes20 sourceAddress; + + @JsonSerialize(using = BytesSerializer.class) + @JsonDeserialize(using = Bytes48Deserializer.class) + public final Bytes48 validatorPublicKey; + + public ExitV1( + @JsonProperty("sourceAddress") final Bytes20 sourceAddress, + @JsonProperty("validatorPublicKey") final Bytes48 validatorPublicKey) { + this.sourceAddress = sourceAddress; + this.validatorPublicKey = validatorPublicKey; + } +} diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/GetPayloadV4Response.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/GetPayloadV4Response.java new file mode 100644 index 00000000000..09d959dbe47 --- /dev/null +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/schema/GetPayloadV4Response.java @@ -0,0 +1,44 @@ +/* + * Copyright Consensys Software Inc., 2022 + * + * 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.executionclient.schema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.apache.tuweni.units.bigints.UInt256; +import tech.pegasys.teku.ethereum.executionclient.serialization.UInt256AsHexDeserializer; +import tech.pegasys.teku.ethereum.executionclient.serialization.UInt256AsHexSerializer; + +public class GetPayloadV4Response { + public final ExecutionPayloadV4 executionPayload; + + @JsonSerialize(using = UInt256AsHexSerializer.class) + @JsonDeserialize(using = UInt256AsHexDeserializer.class) + public final UInt256 blockValue; + + public final BlobsBundleV1 blobsBundle; + + public final boolean shouldOverrideBuilder; + + public GetPayloadV4Response( + @JsonProperty("executionPayload") final ExecutionPayloadV4 executionPayload, + @JsonProperty("blockValue") final UInt256 blockValue, + @JsonProperty("blobsBundle") final BlobsBundleV1 blobsBundle, + @JsonProperty("shouldOverrideBuilder") final boolean shouldOverrideBuilder) { + this.executionPayload = executionPayload; + this.blockValue = blockValue; + this.blobsBundle = blobsBundle; + this.shouldOverrideBuilder = shouldOverrideBuilder; + } +} diff --git a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/web3j/Web3JExecutionEngineClient.java b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/web3j/Web3JExecutionEngineClient.java index 633cf0c55a6..d27e5307b04 100644 --- a/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/web3j/Web3JExecutionEngineClient.java +++ b/ethereum/executionclient/src/main/java/tech/pegasys/teku/ethereum/executionclient/web3j/Web3JExecutionEngineClient.java @@ -31,10 +31,12 @@ import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV1; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV2; import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1; import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV2Response; import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV3Response; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV1; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV2; import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3; @@ -120,6 +122,17 @@ public SafeFuture> getPayloadV3(final Bytes8 payl return web3JClient.doRequest(web3jRequest, EL_ENGINE_NON_BLOCK_EXECUTION_TIMEOUT); } + @Override + public SafeFuture> getPayloadV4(final Bytes8 payloadId) { + final Request web3jRequest = + new Request<>( + "engine_getPayloadV4", + Collections.singletonList(payloadId.toHexString()), + web3JClient.getWeb3jService(), + GetPayloadV4Web3jResponse.class); + return web3JClient.doRequest(web3jRequest, EL_ENGINE_NON_BLOCK_EXECUTION_TIMEOUT); + } + @Override public SafeFuture> newPayloadV1(ExecutionPayloadV1 executionPayload) { final Request web3jRequest = @@ -160,6 +173,23 @@ public SafeFuture> newPayloadV3( return web3JClient.doRequest(web3jRequest, EL_ENGINE_BLOCK_EXECUTION_TIMEOUT); } + @Override + public SafeFuture> newPayloadV4( + final ExecutionPayloadV4 executionPayload, + final List blobVersionedHashes, + final Bytes32 parentBeaconBlockRoot) { + final List expectedBlobVersionedHashes = + blobVersionedHashes.stream().map(VersionedHash::toHexString).toList(); + final Request web3jRequest = + new Request<>( + "engine_newPayloadV4", + list( + executionPayload, expectedBlobVersionedHashes, parentBeaconBlockRoot.toHexString()), + web3JClient.getWeb3jService(), + PayloadStatusV1Web3jResponse.class); + return web3JClient.doRequest(web3jRequest, EL_ENGINE_BLOCK_EXECUTION_TIMEOUT); + } + @Override public SafeFuture> forkChoiceUpdatedV1( ForkChoiceStateV1 forkChoiceState, Optional payloadAttributes) { @@ -230,6 +260,9 @@ static class GetPayloadV2Web3jResponse static class GetPayloadV3Web3jResponse extends org.web3j.protocol.core.Response {} + static class GetPayloadV4Web3jResponse + extends org.web3j.protocol.core.Response {} + static class PayloadStatusV1Web3jResponse extends org.web3j.protocol.core.Response {} diff --git a/ethereum/executionclient/src/test/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineGetPayloadV4Test.java b/ethereum/executionclient/src/test/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineGetPayloadV4Test.java new file mode 100644 index 00000000000..8ae7b63e7b3 --- /dev/null +++ b/ethereum/executionclient/src/test/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineGetPayloadV4Test.java @@ -0,0 +1,155 @@ +/* + * Copyright Consensys Software Inc., 2023 + * + * 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.executionclient.methods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.ethereum.executionclient.ExecutionEngineClient; +import tech.pegasys.teku.ethereum.executionclient.response.InvalidRemoteResponseException; +import tech.pegasys.teku.ethereum.executionclient.schema.BlobsBundleV1; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; +import tech.pegasys.teku.ethereum.executionclient.schema.Response; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.datastructures.execution.BlobsBundle; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadContext; +import tech.pegasys.teku.spec.datastructures.execution.GetPayloadResponse; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionPayloadElectra; +import tech.pegasys.teku.spec.util.DataStructureUtil; + +class EngineGetPayloadV4Test { + + private final Spec spec = TestSpecFactory.createMinimalElectra(); + private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); + private final ExecutionEngineClient executionEngineClient = mock(ExecutionEngineClient.class); + private EngineGetPayloadV4 jsonRpcMethod; + + @BeforeEach + public void setUp() { + jsonRpcMethod = new EngineGetPayloadV4(executionEngineClient, spec); + } + + @Test + public void shouldReturnExpectedNameAndVersion() { + assertThat(jsonRpcMethod.getName()).isEqualTo("engine_getPayload"); + assertThat(jsonRpcMethod.getVersion()).isEqualTo(4); + assertThat(jsonRpcMethod.getVersionedName()).isEqualTo("engine_getPayloadV4"); + } + + @Test + public void executionPayloadContextParamIsRequired() { + final JsonRpcRequestParams params = new JsonRpcRequestParams.Builder().build(); + + assertThatThrownBy(() -> jsonRpcMethod.execute(params)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Missing required parameter at index 0"); + + verifyNoInteractions(executionEngineClient); + } + + @Test + public void slotParamIsRequired() { + final ExecutionPayloadContext executionPayloadContext = + dataStructureUtil.randomPayloadExecutionContext(false); + + final JsonRpcRequestParams params = + new JsonRpcRequestParams.Builder().add(executionPayloadContext).build(); + + assertThatThrownBy(() -> jsonRpcMethod.execute(params)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Missing required parameter at index 1"); + + verifyNoInteractions(executionEngineClient); + } + + @Test + public void shouldReturnFailedExecutionWhenEngineClientRequestFails() { + final ExecutionPayloadContext executionPayloadContext = + dataStructureUtil.randomPayloadExecutionContext(false); + final String errorResponseFromClient = "error!"; + + when(executionEngineClient.getPayloadV4(any())) + .thenReturn(dummyFailedResponse(errorResponseFromClient)); + + final JsonRpcRequestParams params = + new JsonRpcRequestParams.Builder().add(executionPayloadContext).add(UInt64.ZERO).build(); + + assertThat(jsonRpcMethod.execute(params)) + .failsWithin(1, TimeUnit.SECONDS) + .withThrowableOfType(ExecutionException.class) + .withRootCauseInstanceOf(InvalidRemoteResponseException.class) + .withMessageContaining( + "Invalid remote response from the execution client: %s", errorResponseFromClient); + } + + @Test + public void shouldCallGetPayloadV4AndParseResponseSuccessfully() { + final ExecutionPayloadContext executionPayloadContext = + dataStructureUtil.randomPayloadExecutionContext(false); + final UInt256 blockValue = UInt256.MAX_VALUE; + final BlobsBundle blobsBundle = dataStructureUtil.randomBlobsBundle(); + final ExecutionPayload executionPayloadElectra = dataStructureUtil.randomExecutionPayload(); + assertThat(executionPayloadElectra).isInstanceOf(ExecutionPayloadElectra.class); + + when(executionEngineClient.getPayloadV4(eq(executionPayloadContext.getPayloadId()))) + .thenReturn(dummySuccessfulResponse(executionPayloadElectra, blockValue, blobsBundle)); + + final JsonRpcRequestParams params = + new JsonRpcRequestParams.Builder().add(executionPayloadContext).add(UInt64.ZERO).build(); + + jsonRpcMethod = new EngineGetPayloadV4(executionEngineClient, spec); + + final GetPayloadResponse expectedGetPayloadResponse = + new GetPayloadResponse(executionPayloadElectra, blockValue, blobsBundle, false); + assertThat(jsonRpcMethod.execute(params)).isCompletedWithValue(expectedGetPayloadResponse); + + verify(executionEngineClient).getPayloadV4(eq(executionPayloadContext.getPayloadId())); + verifyNoMoreInteractions(executionEngineClient); + } + + private SafeFuture> dummySuccessfulResponse( + final ExecutionPayload executionPayload, + final UInt256 blockValue, + final BlobsBundle blobsBundle) { + return SafeFuture.completedFuture( + new Response<>( + new GetPayloadV4Response( + ExecutionPayloadV4.fromInternalExecutionPayload(executionPayload), + blockValue, + BlobsBundleV1.fromInternalBlobsBundle(blobsBundle), + false))); + } + + private SafeFuture> dummyFailedResponse( + final String errorMessage) { + return SafeFuture.completedFuture(Response.withErrorMessage(errorMessage)); + } +} diff --git a/ethereum/executionclient/src/test/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineNewPayloadV4Test.java b/ethereum/executionclient/src/test/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineNewPayloadV4Test.java new file mode 100644 index 00000000000..c27ea6f9cc6 --- /dev/null +++ b/ethereum/executionclient/src/test/java/tech/pegasys/teku/ethereum/executionclient/methods/EngineNewPayloadV4Test.java @@ -0,0 +1,136 @@ +/* + * Copyright Consensys Software Inc., 2023 + * + * 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.executionclient.methods; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.ethereum.executionclient.ExecutionEngineClient; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; +import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1; +import tech.pegasys.teku.ethereum.executionclient.schema.Response; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.executionlayer.ExecutionPayloadStatus; +import tech.pegasys.teku.spec.executionlayer.PayloadStatus; +import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; +import tech.pegasys.teku.spec.util.DataStructureUtil; + +class EngineNewPayloadV4Test { + + private final Spec spec = TestSpecFactory.createMinimalElectra(); + private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); + private final ExecutionEngineClient executionEngineClient = mock(ExecutionEngineClient.class); + private EngineNewPayloadV4 jsonRpcMethod; + + @BeforeEach + public void setUp() { + jsonRpcMethod = new EngineNewPayloadV4(executionEngineClient); + } + + @Test + public void shouldReturnExpectedNameAndVersion() { + assertThat(jsonRpcMethod.getName()).isEqualTo("engine_newPayload"); + assertThat(jsonRpcMethod.getVersion()).isEqualTo(4); + assertThat(jsonRpcMethod.getVersionedName()).isEqualTo("engine_newPayloadV4"); + } + + @Test + public void executionPayloadParamIsRequired() { + final JsonRpcRequestParams params = new JsonRpcRequestParams.Builder().build(); + + assertThatThrownBy(() -> jsonRpcMethod.execute(params)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Missing required parameter at index 0"); + + verifyNoInteractions(executionEngineClient); + } + + @Test + public void shouldReturnFailedExecutionWhenEngineClientRequestFails() { + final ExecutionPayload executionPayload = dataStructureUtil.randomExecutionPayload(); + final List blobVersionedHashes = dataStructureUtil.randomVersionedHashes(3); + final Bytes32 parentBeaconBlockRoot = dataStructureUtil.randomBytes32(); + final String errorResponseFromClient = "error!"; + + when(executionEngineClient.newPayloadV4(any(), any(), any())) + .thenReturn(dummyFailedResponse(errorResponseFromClient)); + + final JsonRpcRequestParams params = + new JsonRpcRequestParams.Builder() + .add(executionPayload) + .add(blobVersionedHashes) + .add(parentBeaconBlockRoot) + .build(); + + assertThat(jsonRpcMethod.execute(params)) + .succeedsWithin(1, TimeUnit.SECONDS) + .matches(PayloadStatus::hasFailedExecution); + } + + @Test + public void shouldCallNewPayloadV4WithExecutionPayloadV4AndBlobVersionedHashes() { + final ExecutionPayload executionPayload = dataStructureUtil.randomExecutionPayload(); + final List blobVersionedHashes = dataStructureUtil.randomVersionedHashes(4); + final Bytes32 parentBeaconBlockRoot = dataStructureUtil.randomBytes32(); + + final ExecutionPayloadV4 executionPayloadV4 = + ExecutionPayloadV4.fromInternalExecutionPayload(executionPayload); + assertThat(executionPayloadV4).isExactlyInstanceOf(ExecutionPayloadV4.class); + + jsonRpcMethod = new EngineNewPayloadV4(executionEngineClient); + + when(executionEngineClient.newPayloadV4( + executionPayloadV4, blobVersionedHashes, parentBeaconBlockRoot)) + .thenReturn(dummySuccessfulResponse()); + + final JsonRpcRequestParams params = + new JsonRpcRequestParams.Builder() + .add(executionPayload) + .add(blobVersionedHashes) + .add(parentBeaconBlockRoot) + .build(); + + assertThat(jsonRpcMethod.execute(params)).isCompleted(); + + verify(executionEngineClient) + .newPayloadV4(eq(executionPayloadV4), eq(blobVersionedHashes), eq(parentBeaconBlockRoot)); + verifyNoMoreInteractions(executionEngineClient); + } + + private SafeFuture> dummySuccessfulResponse() { + return SafeFuture.completedFuture( + new Response<>( + new PayloadStatusV1( + ExecutionPayloadStatus.ACCEPTED, dataStructureUtil.randomBytes32(), null))); + } + + private SafeFuture> dummyFailedResponse(final String errorMessage) { + return SafeFuture.completedFuture(Response.withErrorMessage(errorMessage)); + } +} diff --git a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java index fd3d0f9d3b0..d388e3d86c4 100644 --- a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java +++ b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerBlockProductionManagerImpl.java @@ -78,7 +78,7 @@ public ExecutionPayloadResult initiateBlockProduction( if (!isBlind) { final SafeFuture getPayloadResponseFuture = executionLayerChannel - .engineGetPayload(context, blockSlotState.getSlot()) + .engineGetPayload(context, blockSlotState) .thenPeek(__ -> blockProductionPerformance.engineGetPayload()); final SafeFuture executionPayloadFuture = getPayloadResponseFuture.thenApply(GetPayloadResponse::getExecutionPayload); @@ -111,7 +111,7 @@ public ExecutionPayloadResult initiateBlockAndBlobsProduction( if (!isBlind) { final SafeFuture getPayloadResponseFuture = executionLayerChannel - .engineGetPayload(context, blockSlotState.getSlot()) + .engineGetPayload(context, blockSlotState) .thenPeek(__ -> blockProductionPerformance.engineGetPayload()); final SafeFuture executionPayloadFuture = getPayloadResponseFuture.thenApply(GetPayloadResponse::getExecutionPayload); diff --git a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImpl.java b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImpl.java index b494f608255..75847ebd7f9 100644 --- a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImpl.java +++ b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImpl.java @@ -195,8 +195,8 @@ public SafeFuture engineForkChoiceUpdated( @Override public SafeFuture engineGetPayload( - final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) { - return engineGetPayload(executionPayloadContext, slot, false) + final ExecutionPayloadContext executionPayloadContext, final BeaconState state) { + return engineGetPayload(executionPayloadContext, state.getSlot(), false) .thenPeek(__ -> recordExecutionPayloadFallbackSource(Source.LOCAL_EL, FallbackReason.NONE)); } diff --git a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerStub.java b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerStub.java index 7fedfb01991..2792225f45c 100644 --- a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerStub.java +++ b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerStub.java @@ -37,9 +37,9 @@ public class ExecutionLayerManagerStub extends ExecutionLayerChannelStub private final BuilderCircuitBreaker builderCircuitBreaker; public ExecutionLayerManagerStub( - Spec spec, - TimeProvider timeProvider, - boolean enableTransitionEmulation, + final Spec spec, + final TimeProvider timeProvider, + final boolean enableTransitionEmulation, final Optional terminalBlockHashInTTDMode, final BuilderCircuitBreaker builderCircuitBreaker) { super(spec, timeProvider, enableTransitionEmulation, terminalBlockHashInTTDMode); @@ -58,7 +58,7 @@ public SafeFuture builderGetHeader( final SafeFuture payloadValueResult, final Optional requestedBuilderBoostFactor, final BlockProductionPerformance blockProductionPerformance) { - boolean builderCircuitBreakerEngaged = builderCircuitBreaker.isEngaged(state); + final boolean builderCircuitBreakerEngaged = builderCircuitBreaker.isEngaged(state); LOG.info("Builder Circuit Breaker isEngaged: " + builderCircuitBreakerEngaged); return super.builderGetHeader( @@ -70,9 +70,7 @@ public SafeFuture builderGetHeader( .thenCompose( headerWithFallbackData -> { if (builderCircuitBreakerEngaged) { - return engineGetPayload( - executionPayloadContext, - executionPayloadContext.getPayloadBuildingAttributes().getProposalSlot()) + return engineGetPayload(executionPayloadContext, state) .thenApply( payload -> HeaderWithFallbackData.create( diff --git a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolver.java b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolver.java index bb858ed6b59..22b8d6aab57 100644 --- a/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolver.java +++ b/ethereum/executionlayer/src/main/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolver.java @@ -32,10 +32,12 @@ import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV1; import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV2; import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV4; import tech.pegasys.teku.ethereum.executionclient.methods.EngineJsonRpcMethod; import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV1; import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV2; import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV4; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.util.ForkAndSpecMilestone; @@ -111,8 +113,8 @@ private Map> denebSupportedMethods() { private Map> electraSupportedMethods() { final Map> methods = new HashMap<>(); - methods.put(ENGINE_NEW_PAYLOAD, new EngineNewPayloadV3(executionEngineClient)); - methods.put(ENGINE_GET_PAYLOAD, new EngineGetPayloadV3(executionEngineClient, spec)); + methods.put(ENGINE_NEW_PAYLOAD, new EngineNewPayloadV4(executionEngineClient)); + methods.put(ENGINE_GET_PAYLOAD, new EngineGetPayloadV4(executionEngineClient, spec)); methods.put(ENGINE_FORK_CHOICE_UPDATED, new EngineForkChoiceUpdatedV3(executionEngineClient)); return methods; diff --git a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ElectraExecutionClientHandlerTest.java b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ElectraExecutionClientHandlerTest.java new file mode 100644 index 00000000000..7e940fbf04f --- /dev/null +++ b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ElectraExecutionClientHandlerTest.java @@ -0,0 +1,143 @@ +/* + * Copyright Consensys Software Inc., 2022 + * + * 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 static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import org.apache.tuweni.bytes.Bytes32; +import org.apache.tuweni.units.bigints.UInt256; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.ethereum.executionclient.schema.BlobsBundleV1; +import tech.pegasys.teku.ethereum.executionclient.schema.ExecutionPayloadV4; +import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceStateV1; +import tech.pegasys.teku.ethereum.executionclient.schema.ForkChoiceUpdatedResult; +import tech.pegasys.teku.ethereum.executionclient.schema.GetPayloadV4Response; +import tech.pegasys.teku.ethereum.executionclient.schema.PayloadAttributesV3; +import tech.pegasys.teku.ethereum.executionclient.schema.PayloadStatusV1; +import tech.pegasys.teku.ethereum.executionclient.schema.Response; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadContext; +import tech.pegasys.teku.spec.datastructures.execution.GetPayloadResponse; +import tech.pegasys.teku.spec.datastructures.execution.NewPayloadRequest; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionPayloadElectra; +import tech.pegasys.teku.spec.executionlayer.ExecutionPayloadStatus; +import tech.pegasys.teku.spec.executionlayer.ForkChoiceState; +import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes; +import tech.pegasys.teku.spec.executionlayer.PayloadStatus; +import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; +import tech.pegasys.teku.spec.util.DataStructureUtil; + +public class ElectraExecutionClientHandlerTest extends ExecutionHandlerClientTest { + + @BeforeEach + void setup() { + spec = TestSpecFactory.createMinimalElectra(); + dataStructureUtil = new DataStructureUtil(spec); + } + + @Test + void engineGetPayload_shouldCallGetPayloadV4() throws ExecutionException, InterruptedException { + final ExecutionClientHandler handler = getHandler(); + final ExecutionPayloadContext context = randomContext(); + final SafeFuture> dummyResponse = + SafeFuture.completedFuture( + new Response<>( + new GetPayloadV4Response( + ExecutionPayloadV4.fromInternalExecutionPayload( + dataStructureUtil.randomExecutionPayload()), + UInt256.MAX_VALUE, + BlobsBundleV1.fromInternalBlobsBundle(dataStructureUtil.randomBlobsBundle()), + true))); + when(executionEngineClient.getPayloadV4(context.getPayloadId())).thenReturn(dummyResponse); + + final UInt64 slot = dataStructureUtil.randomUInt64(1_000_000); + final SafeFuture future = handler.engineGetPayload(context, slot); + verify(executionEngineClient).getPayloadV4(context.getPayloadId()); + assertThat(future).isCompleted(); + assertThat(future.get().getExecutionPayload()).isInstanceOf(ExecutionPayloadElectra.class); + assertThat(future.get().getExecutionPayloadValue()).isEqualTo(UInt256.MAX_VALUE); + assertThat(future.get().getBlobsBundle()).isPresent(); + assertThat(future.get().getShouldOverrideBuilder()).isTrue(); + } + + @Test + void engineNewPayload_shouldCallNewPayloadV4() { + final ExecutionClientHandler handler = getHandler(); + final ExecutionPayload payload = dataStructureUtil.randomExecutionPayload(); + final Bytes32 parentBeaconBlockRoot = dataStructureUtil.randomBytes32(); + final List versionedHashes = dataStructureUtil.randomVersionedHashes(3); + final NewPayloadRequest newPayloadRequest = + new NewPayloadRequest(payload, versionedHashes, parentBeaconBlockRoot); + final ExecutionPayloadV4 payloadV4 = ExecutionPayloadV4.fromInternalExecutionPayload(payload); + final SafeFuture> dummyResponse = + SafeFuture.completedFuture( + new Response<>( + new PayloadStatusV1( + ExecutionPayloadStatus.ACCEPTED, dataStructureUtil.randomBytes32(), null))); + when(executionEngineClient.newPayloadV4(payloadV4, versionedHashes, parentBeaconBlockRoot)) + .thenReturn(dummyResponse); + final SafeFuture future = handler.engineNewPayload(newPayloadRequest); + verify(executionEngineClient).newPayloadV4(payloadV4, versionedHashes, parentBeaconBlockRoot); + assertThat(future).isCompleted(); + } + + @Test + void engineForkChoiceUpdated_shouldCallEngineForkChoiceUpdatedV3() { + final ExecutionClientHandler handler = getHandler(); + final ForkChoiceState forkChoiceState = dataStructureUtil.randomForkChoiceState(false); + final ForkChoiceStateV1 forkChoiceStateV1 = + ForkChoiceStateV1.fromInternalForkChoiceState(forkChoiceState); + final PayloadBuildingAttributes attributes = + new PayloadBuildingAttributes( + dataStructureUtil.randomUInt64(), + dataStructureUtil.randomUInt64(), + dataStructureUtil.randomUInt64(), + dataStructureUtil.randomBytes32(), + dataStructureUtil.randomEth1Address(), + Optional.empty(), + Optional.of(List.of()), + dataStructureUtil.randomBytes32()); + final Optional payloadAttributes = + PayloadAttributesV3.fromInternalPayloadBuildingAttributesV3(Optional.of(attributes)); + final SafeFuture> dummyResponse = + SafeFuture.completedFuture( + new Response<>( + new ForkChoiceUpdatedResult( + new PayloadStatusV1( + ExecutionPayloadStatus.ACCEPTED, dataStructureUtil.randomBytes32(), ""), + dataStructureUtil.randomBytes8()))); + when(executionEngineClient.forkChoiceUpdatedV3(forkChoiceStateV1, payloadAttributes)) + .thenReturn(dummyResponse); + final SafeFuture future = + handler.engineForkChoiceUpdated(forkChoiceState, Optional.of(attributes)); + verify(executionEngineClient).forkChoiceUpdatedV3(forkChoiceStateV1, payloadAttributes); + assertThat(future).isCompleted(); + } + + private ExecutionPayloadContext randomContext() { + return new ExecutionPayloadContext( + dataStructureUtil.randomBytes8(), + dataStructureUtil.randomForkChoiceState(false), + dataStructureUtil.randomPayloadBuildingAttributes(false)); + } +} diff --git a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImplTest.java b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImplTest.java index 1423547e425..f6cc7ac0c7d 100644 --- a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImplTest.java +++ b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/ExecutionLayerManagerImplTest.java @@ -166,11 +166,12 @@ public void engineGetPayload_shouldReturnGetPayloadResponseViaEngine() { final ExecutionPayloadContext executionPayloadContext = dataStructureUtil.randomPayloadExecutionContext(false, true); final UInt64 slot = executionPayloadContext.getForkChoiceState().getHeadBlockSlot(); + final BeaconState state = dataStructureUtil.randomBeaconState(slot); final GetPayloadResponse getPayloadResponse = prepareEngineGetPayloadResponse(executionPayloadContext, localExecutionPayloadValue, slot); - assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, slot)) + assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, state)) .isCompletedWithValue(getPayloadResponse); // we expect no calls to builder @@ -187,11 +188,12 @@ public void engineGetPayloadV2_shouldReturnPayloadViaEngine() { dataStructureUtil.randomPayloadExecutionContext(false, true); executionLayerManager = createExecutionLayerChannelImpl(false, false); final UInt64 slot = executionPayloadContext.getForkChoiceState().getHeadBlockSlot(); + final BeaconState state = dataStructureUtil.randomBeaconState(slot); final GetPayloadResponse getPayloadResponse = prepareEngineGetPayloadResponse(executionPayloadContext, localExecutionPayloadValue, slot); - assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, slot)) + assertThat(executionLayerManager.engineGetPayload(executionPayloadContext, state)) .isCompletedWithValue(getPayloadResponse); // we expect no calls to builder diff --git a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolverTest.java b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolverTest.java index 750821a2bc1..be9921cc3ce 100644 --- a/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolverTest.java +++ b/ethereum/executionlayer/src/test/java/tech/pegasys/teku/ethereum/executionlayer/MilestoneBasedEngineJsonRpcMethodsResolverTest.java @@ -36,10 +36,12 @@ import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV1; import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV2; import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.methods.EngineGetPayloadV4; import tech.pegasys.teku.ethereum.executionclient.methods.EngineJsonRpcMethod; import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV1; import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV2; import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV3; +import tech.pegasys.teku.ethereum.executionclient.methods.EngineNewPayloadV4; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; @@ -193,15 +195,16 @@ void shouldProvideExpectedMethodsForElectra( private static Stream electraMethods() { return Stream.of( - arguments(ENGINE_NEW_PAYLOAD, EngineNewPayloadV3.class), - arguments(ENGINE_GET_PAYLOAD, EngineGetPayloadV3.class), + arguments(ENGINE_NEW_PAYLOAD, EngineNewPayloadV4.class), + arguments(ENGINE_GET_PAYLOAD, EngineGetPayloadV4.class), arguments(ENGINE_FORK_CHOICE_UPDATED, EngineForkChoiceUpdatedV3.class)); } @Test void getsCapabilities() { final Spec spec = - TestSpecFactory.createMinimalWithCapellaAndDenebForkEpoch(UInt64.ONE, UInt64.valueOf(2)); + TestSpecFactory.createMinimalWithCapellaDenebAndElectraForkEpoch( + UInt64.ONE, UInt64.valueOf(2), UInt64.valueOf(3)); final MilestoneBasedEngineJsonRpcMethodsResolver engineMethodsResolver = new MilestoneBasedEngineJsonRpcMethodsResolver(spec, executionEngineClient); @@ -218,6 +221,8 @@ void getsCapabilities() { "engine_forkchoiceUpdatedV2", "engine_newPayloadV3", "engine_getPayloadV3", - "engine_forkchoiceUpdatedV3"); + "engine_forkchoiceUpdatedV3", + "engine_newPayloadV4", + "engine_getPayloadV4"); } } diff --git a/ethereum/json-types/src/main/java/tech/pegasys/teku/ethereum/json/types/EthereumTypes.java b/ethereum/json-types/src/main/java/tech/pegasys/teku/ethereum/json/types/EthereumTypes.java index fe635eeda04..7fac43c10aa 100644 --- a/ethereum/json-types/src/main/java/tech/pegasys/teku/ethereum/json/types/EthereumTypes.java +++ b/ethereum/json-types/src/main/java/tech/pegasys/teku/ethereum/json/types/EthereumTypes.java @@ -25,6 +25,7 @@ import tech.pegasys.teku.ethereum.execution.types.Eth1Address; import tech.pegasys.teku.infrastructure.http.RestApiConstants; import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; +import tech.pegasys.teku.infrastructure.json.types.EnumTypeDefinition; import tech.pegasys.teku.infrastructure.json.types.StringValueTypeDefinition; import tech.pegasys.teku.infrastructure.restapi.openapi.response.OctetStreamResponseContentTypeDefinition; import tech.pegasys.teku.infrastructure.restapi.openapi.response.ResponseContentTypeDefinition; @@ -91,8 +92,8 @@ public class EthereumTypes { .format("byte") .build(); - public static final DeserializableTypeDefinition MILESTONE_TYPE = - DeserializableTypeDefinition.enumOf( + public static final StringValueTypeDefinition MILESTONE_TYPE = + new EnumTypeDefinition<>( SpecMilestone.class, milestone -> milestone.name().toLowerCase(Locale.ROOT), Set.of()); public static > diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java index 896a1e42729..695442c4cd5 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java @@ -42,7 +42,6 @@ import tech.pegasys.teku.infrastructure.bytes.Bytes4; import tech.pegasys.teku.infrastructure.ssz.Merkleizable; import tech.pegasys.teku.infrastructure.ssz.SszList; -import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.cache.IndexedAttestationCache; import tech.pegasys.teku.spec.config.NetworkingSpecConfig; @@ -91,7 +90,6 @@ import tech.pegasys.teku.spec.logic.common.block.BlockProcessor; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; import tech.pegasys.teku.spec.logic.common.operations.validation.OperationInvalidReason; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.BlockProcessingException; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.EpochProcessingException; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.SlotProcessingException; @@ -869,8 +867,8 @@ public Map getValidatorIndexToCommitteeAssignmentM } // Attestation helpers - public IntList getAttestingIndices(BeaconState state, AttestationData data, SszBitlist bits) { - return atState(state).getAttestationUtil().getAttestingIndices(state, data, bits); + public IntList getAttestingIndices(final BeaconState state, final Attestation attestation) { + return atState(state).getAttestationUtil().getAttestingIndices(state, attestation); } public AttestationData getGenericAttestationData( @@ -894,10 +892,6 @@ public SafeFuture isValidIndexedAttestation( getForkAtSlot(slot), state, attestation, blsSignatureVerifier); } - public AttestationWorthinessChecker createAttestationWorthinessChecker(final BeaconState state) { - return atState(state).getAttestationUtil().createAttestationWorthinessChecker(state); - } - public boolean isMergeTransitionComplete(final BeaconState state) { return atState(state).miscHelpers().isMergeTransitionComplete(state); } @@ -940,6 +934,11 @@ public Optional computeFirstSlotWithBlobSupport() { .map(this::computeStartSlotAtEpoch); } + // Electra Utils + public boolean isFormerDepositMechanismDisabled(BeaconState state) { + return atState(state).miscHelpers().isFormerDepositMechanismDisabled(state); + } + // Deneb private helpers private Optional getSpecConfigDeneb() { final SpecMilestone highestSupportedMilestone = diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayload.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayload.java index 678019f833b..848ca04357a 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayload.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayload.java @@ -25,7 +25,6 @@ import tech.pegasys.teku.spec.datastructures.execution.versions.capella.ExecutionPayloadCapella; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.Withdrawal; import tech.pegasys.teku.spec.datastructures.execution.versions.deneb.ExecutionPayloadDeneb; -import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerExit; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionPayloadElectra; public interface ExecutionPayload extends ExecutionPayloadSummary, SszContainer, BuilderPayload { @@ -39,10 +38,6 @@ default Optional> getOptionalWithdrawals() { return Optional.empty(); } - default Optional> getOptionalExits() { - return Optional.empty(); - } - /** * getUnblindedTreeNodes * diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayloadSchema.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayloadSchema.java index 1eb8b0655e5..c297285abfb 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayloadSchema.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/ExecutionPayloadSchema.java @@ -22,6 +22,7 @@ import tech.pegasys.teku.spec.datastructures.builder.BuilderPayloadSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.Withdrawal; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.WithdrawalSchema; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceiptSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerExitSchema; @@ -37,6 +38,9 @@ public interface ExecutionPayloadSchema WithdrawalSchema getWithdrawalSchemaRequired(); + SszListSchema> + getDepositReceiptsSchemaRequired(); + DepositReceiptSchema getDepositReceiptSchemaRequired(); ExecutionLayerExitSchema getExecutionLayerExitSchemaRequired(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/bellatrix/ExecutionPayloadSchemaBellatrix.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/bellatrix/ExecutionPayloadSchemaBellatrix.java index c9647126a82..be750d29944 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/bellatrix/ExecutionPayloadSchemaBellatrix.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/bellatrix/ExecutionPayloadSchemaBellatrix.java @@ -51,6 +51,7 @@ import tech.pegasys.teku.spec.datastructures.execution.TransactionSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.Withdrawal; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.WithdrawalSchema; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceiptSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerExitSchema; @@ -114,6 +115,12 @@ public WithdrawalSchema getWithdrawalSchemaRequired() { throw new IllegalStateException("Attempted to get a withdrawal schema from bellatrix"); } + @Override + public SszListSchema> + getDepositReceiptsSchemaRequired() { + throw new IllegalStateException("Attempted to get a deposit receipts schema from bellatrix"); + } + @Override public DepositReceiptSchema getDepositReceiptSchemaRequired() { throw new IllegalStateException("Attempted to get a deposit receipt schema from bellatrix"); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/capella/ExecutionPayloadSchemaCapella.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/capella/ExecutionPayloadSchemaCapella.java index f4b7d448f20..640d4d5aed0 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/capella/ExecutionPayloadSchemaCapella.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/capella/ExecutionPayloadSchemaCapella.java @@ -50,6 +50,7 @@ import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayloadSchema; import tech.pegasys.teku.spec.datastructures.execution.Transaction; import tech.pegasys.teku.spec.datastructures.execution.TransactionSchema; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceiptSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerExitSchema; @@ -121,6 +122,12 @@ public WithdrawalSchema getWithdrawalSchemaRequired() { return getWithdrawalSchema(); } + @Override + public SszListSchema> + getDepositReceiptsSchemaRequired() { + throw new IllegalStateException("Attempted to get a deposit receipts schema from capella"); + } + @Override public DepositReceiptSchema getDepositReceiptSchemaRequired() { throw new IllegalStateException("Attempted to get a deposit receipt schema from capella"); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/deneb/ExecutionPayloadSchemaDeneb.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/deneb/ExecutionPayloadSchemaDeneb.java index e30dcd66d28..c1e01b37fd9 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/deneb/ExecutionPayloadSchemaDeneb.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/deneb/ExecutionPayloadSchemaDeneb.java @@ -54,6 +54,7 @@ import tech.pegasys.teku.spec.datastructures.execution.TransactionSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.Withdrawal; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.WithdrawalSchema; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceiptSchema; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionLayerExitSchema; @@ -129,6 +130,12 @@ public WithdrawalSchema getWithdrawalSchemaRequired() { return getWithdrawalSchema(); } + @Override + public SszListSchema> + getDepositReceiptsSchemaRequired() { + throw new IllegalStateException("Attempted to get a deposit receipts schema from deneb"); + } + @Override public DepositReceiptSchema getDepositReceiptSchemaRequired() { throw new IllegalStateException("Attempted to get a deposit receipt schema from deneb"); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/electra/ExecutionPayloadSchemaElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/electra/ExecutionPayloadSchemaElectra.java index 885956acdbe..211beab72b7 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/electra/ExecutionPayloadSchemaElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/versions/electra/ExecutionPayloadSchemaElectra.java @@ -139,6 +139,12 @@ public WithdrawalSchema getWithdrawalSchemaRequired() { return getWithdrawalSchema(); } + @Override + public SszListSchema> + getDepositReceiptsSchemaRequired() { + return getDepositReceiptsSchema(); + } + @Override public DepositReceiptSchema getDepositReceiptSchemaRequired() { return getDepositReceiptSchema(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java index d6bf69e5c2d..f0ee74a4fdc 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/Attestation.java @@ -28,8 +28,8 @@ import tech.pegasys.teku.spec.datastructures.type.SszSignature; import tech.pegasys.teku.spec.datastructures.type.SszSignatureSchema; -public class Attestation - extends Container3 { +public class Attestation extends Container3 + implements AttestationContainer { public static class AttestationSchema extends ContainerSchema3 { @@ -91,10 +91,12 @@ public Collection getDependentBlockRoots() { return Sets.newHashSet(getData().getTarget().getRoot(), getData().getBeaconBlockRoot()); } + @Override public SszBitlist getAggregationBits() { return getField0(); } + @Override public AttestationData getData() { return getField1(); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationContainer.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationContainer.java new file mode 100644 index 00000000000..125bad285d6 --- /dev/null +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/operations/AttestationContainer.java @@ -0,0 +1,29 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.spec.datastructures.operations; + +import tech.pegasys.teku.infrastructure.ssz.SszContainer; +import tech.pegasys.teku.infrastructure.ssz.SszData; +import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; + +/** + * Interface used to represent different types of attestations ({@link Attestation} and {@link + * tech.pegasys.teku.spec.datastructures.state.PendingAttestation}) + */ +public interface AttestationContainer extends SszData, SszContainer { + + AttestationData getData(); + + SszBitlist getAggregationBits(); +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/PendingAttestation.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/PendingAttestation.java index 9a5c6b4c1aa..a293bf22bd3 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/PendingAttestation.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/PendingAttestation.java @@ -22,10 +22,12 @@ import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; +import tech.pegasys.teku.spec.datastructures.operations.AttestationContainer; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; public class PendingAttestation - extends Container4 { + extends Container4 + implements AttestationContainer { public static class PendingAttestationSchema extends ContainerSchema4< @@ -82,10 +84,12 @@ public PendingAttestationSchema getSchema() { return (PendingAttestationSchema) super.getSchema(); } + @Override public SszBitlist getAggregationBits() { return getField0(); } + @Override public AttestationData getData() { return getField1(); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/MutableBeaconStateElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/MutableBeaconStateElectra.java index 20155013ae7..7e03486adad 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/MutableBeaconStateElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/MutableBeaconStateElectra.java @@ -16,10 +16,19 @@ import java.util.Optional; import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.MutableBeaconState; import tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.deneb.MutableBeaconStateDeneb; public interface MutableBeaconStateElectra extends MutableBeaconStateDeneb, BeaconStateElectra { + static MutableBeaconStateElectra required(final MutableBeaconState state) { + return state + .toMutableVersionElectra() + .orElseThrow( + () -> + new IllegalArgumentException( + "Expected an Electra state but got: " + state.getClass().getSimpleName())); + } @Override BeaconStateElectra commitChanges(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/util/DepositReceiptsUtil.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/util/DepositReceiptsUtil.java new file mode 100644 index 00000000000..36a98bfeacd --- /dev/null +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/util/DepositReceiptsUtil.java @@ -0,0 +1,73 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.spec.datastructures.util; + +import java.security.SecureRandom; +import java.util.List; +import java.util.stream.IntStream; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.bls.BLS; +import tech.pegasys.teku.bls.BLSKeyPair; +import tech.pegasys.teku.bls.BLSPublicKey; +import tech.pegasys.teku.bls.BLSSignature; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.constants.Domain; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; +import tech.pegasys.teku.spec.datastructures.operations.DepositMessage; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; + +public class DepositReceiptsUtil { + + private static final int MAX_NUMBER_OF_DEPOSITS_PER_BLOCK = 3; + + private final Spec spec; + + @SuppressWarnings("DoNotCreateSecureRandomDirectly") + private final SecureRandom random = new SecureRandom(); + + public DepositReceiptsUtil(final Spec spec) { + this.spec = spec; + } + + public List generateDepositReceipts(final BeaconState state) { + final UInt64 nextDepositReceiptIndex = UInt64.valueOf(state.getValidators().size()); + return IntStream.range(0, getNumberOfDepositReceiptsToGenerate()) + .mapToObj(i -> createDepositReceipt(state.getSlot(), nextDepositReceiptIndex.plus(i))) + .toList(); + } + + private int getNumberOfDepositReceiptsToGenerate() { + return random.nextInt(MAX_NUMBER_OF_DEPOSITS_PER_BLOCK + 1); + } + + private DepositReceipt createDepositReceipt(final UInt64 slot, final UInt64 index) { + final BLSKeyPair validatorKeyPair = BLSKeyPair.random(random); + final BLSPublicKey publicKey = validatorKeyPair.getPublicKey(); + final UInt64 depositAmount = UInt64.THIRTY_TWO_ETH; + final DepositMessage depositMessage = + new DepositMessage(publicKey, Bytes32.ZERO, depositAmount); + final MiscHelpers miscHelpers = spec.atSlot(slot).miscHelpers(); + final Bytes32 depositDomain = miscHelpers.computeDomain(Domain.DEPOSIT); + final BLSSignature signature = + BLS.sign( + validatorKeyPair.getSecretKey(), + miscHelpers.computeSigningRoot(depositMessage, depositDomain)); + return SchemaDefinitionsElectra.required(spec.atSlot(slot).getSchemaDefinitions()) + .getDepositReceiptSchema() + .create(publicKey, Bytes32.ZERO, depositAmount, signature, index); + } +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannel.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannel.java index 336ddf3141e..3199e384002 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannel.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannel.java @@ -60,7 +60,7 @@ public SafeFuture engineForkChoiceUpdated( @Override public SafeFuture engineGetPayload( - final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) { + final ExecutionPayloadContext executionPayloadContext, final BeaconState state) { return SafeFuture.completedFuture(null); } @@ -123,7 +123,7 @@ SafeFuture engineForkChoiceUpdated( * BeaconState, boolean, Optional, BlockProductionPerformance)} instead */ SafeFuture engineGetPayload( - ExecutionPayloadContext executionPayloadContext, UInt64 slot); + ExecutionPayloadContext executionPayloadContext, BeaconState state); // builder namespace SafeFuture builderRegisterValidators( diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannelStub.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannelStub.java index 255066122a0..8d1decb640f 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannelStub.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/executionlayer/ExecutionLayerChannelStub.java @@ -61,9 +61,11 @@ import tech.pegasys.teku.spec.datastructures.execution.HeaderWithFallbackData; import tech.pegasys.teku.spec.datastructures.execution.NewPayloadRequest; import tech.pegasys.teku.spec.datastructures.execution.PowBlock; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; import tech.pegasys.teku.spec.datastructures.util.BlobsUtil; +import tech.pegasys.teku.spec.datastructures.util.DepositReceiptsUtil; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; @@ -73,6 +75,8 @@ public class ExecutionLayerChannelStub implements ExecutionLayerChannel { private static final ClientVersion STUB_CLIENT_VERSION = new ClientVersion("SB", ExecutionLayerChannel.STUB_ENDPOINT_PREFIX, "0.0.0", Bytes4.ZERO); + private static final boolean GENERATE_DEPOSIT_RECEIPTS = false; + private final TimeProvider timeProvider; private final Map knownBlocks = new ConcurrentHashMap<>(); private final Map knownPosBlocks = new ConcurrentHashMap<>(); @@ -81,6 +85,7 @@ public class ExecutionLayerChannelStub implements ExecutionLayerChannel { private final Set requestedPowBlocks = new HashSet<>(); private final Spec spec; private final BlobsUtil blobsUtil; + private final DepositReceiptsUtil depositReceiptsUtil; private final Random random = new Random(); private PayloadStatus payloadStatus = PayloadStatus.VALID; @@ -122,6 +127,7 @@ public ExecutionLayerChannelStub( kzg = KZG.NOOP; } this.blobsUtil = new BlobsUtil(spec, kzg); + this.depositReceiptsUtil = new DepositReceiptsUtil(spec); } public ExecutionLayerChannelStub( @@ -222,7 +228,7 @@ public SafeFuture engineForkChoiceUpdated( @Override public SafeFuture engineGetPayload( - final ExecutionPayloadContext executionPayloadContext, final UInt64 slot) { + final ExecutionPayloadContext executionPayloadContext, final BeaconState state) { if (!bellatrixActivationDetected) { LOG.info( "getPayload received before terminalBlock has been sent. Assuming transition already happened"); @@ -230,6 +236,7 @@ public SafeFuture engineGetPayload( // do the activation check to be able to respond to terminal block verification checkBellatrixActivation(); } + final UInt64 slot = state.getSlot(); final Optional schemaDefinitionsBellatrix = spec.atSlot(slot).getSchemaDefinitions().toVersionBellatrix(); @@ -246,9 +253,7 @@ public SafeFuture engineGetPayload( final List transactions = generateTransactions(slot, headAndAttrs); final ExecutionPayload executionPayload = - spec.atSlot(slot) - .getSchemaDefinitions() - .toVersionBellatrix() + schemaDefinitionsBellatrix .orElseThrow() .getExecutionPayloadSchema() .createExecutionPayload( @@ -270,7 +275,9 @@ public SafeFuture engineGetPayload( .transactions(transactions) .withdrawals(() -> payloadAttributes.getWithdrawals().orElse(List.of())) .blobGasUsed(() -> UInt64.ZERO) - .excessBlobGas(() -> UInt64.ZERO)); + .excessBlobGas(() -> UInt64.ZERO) + .depositReceipts(() -> generateDepositReceipts(state)) + .exits(List::of)); // we assume all blocks are produced locally lastValidBlock = @@ -286,7 +293,7 @@ public SafeFuture engineGetPayload( LOG.info( "getPayload: payloadId: {} slot: {} -> executionPayload blockHash: {}", executionPayloadContext.getPayloadId(), - slot, + state.getSlot(), executionPayload.getBlockHash()); final GetPayloadResponse getPayloadResponse = @@ -344,7 +351,7 @@ public SafeFuture builderGetHeader( final SchemaDefinitions schemaDefinitions = spec.atSlot(slot).getSchemaDefinitions(); - return engineGetPayload(executionPayloadContext, slot) + return engineGetPayload(executionPayloadContext, state) .thenPeek(__ -> blockProductionPerformance.engineGetPayload()) .thenApply( getPayloadResponse -> { @@ -552,4 +559,18 @@ private Bytes generateBlobsAndTransaction( return blobsUtil.generateRawBlobTransactionFromKzgCommitments(commitments); } + + private List generateDepositReceipts(final BeaconState state) { + return spec.atSlot(state.getSlot()) + .getConfig() + .toVersionElectra() + .map( + __ -> { + if (GENERATE_DEPOSIT_RECEIPTS) { + return depositReceiptsUtil.generateDepositReceipts(state); + } + return List.of(); + }) + .orElse(List.of()); + } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/AbstractBlockProcessor.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/AbstractBlockProcessor.java index 6b2c72c4857..b68d10138b6 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/AbstractBlockProcessor.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/AbstractBlockProcessor.java @@ -427,22 +427,11 @@ protected void processOperationsNoValidation( throws BlockProcessingException { safelyProcess( () -> { - final int expectedDepositCount = - Math.min( - specConfig.getMaxDeposits(), - state - .getEth1Data() - .getDepositCount() - .minus(state.getEth1DepositIndex()) - .intValue()); + verifyOutstandingDepositsAreProcessed(state, body); final Supplier validatorExitContextSupplier = beaconStateMutators.createValidatorExitContextSupplier(state); - checkArgument( - body.getDeposits().size() == expectedDepositCount, - "process_operations: Verify that outstanding deposits are processed up to the maximum number of deposits"); - processProposerSlashingsNoValidation( state, body.getProposerSlashings(), validatorExitContextSupplier); processAttesterSlashings( @@ -451,10 +440,21 @@ protected void processOperationsNoValidation( processDeposits(state, body.getDeposits()); processVoluntaryExitsNoValidation( state, body.getVoluntaryExits(), validatorExitContextSupplier); - // @process_shard_receipt_proofs }); } + protected void verifyOutstandingDepositsAreProcessed( + final BeaconState state, final BeaconBlockBody body) { + final int expectedDepositCount = + Math.min( + specConfig.getMaxDeposits(), + state.getEth1Data().getDepositCount().minus(state.getEth1DepositIndex()).intValue()); + + checkArgument( + body.getDeposits().size() == expectedDepositCount, + "process_operations: Verify that outstanding deposits are processed up to the maximum number of deposits"); + } + @Override public void processProposerSlashings( final MutableBeaconState state, diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpers.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpers.java index 364da862ea6..ace2a21ac51 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpers.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpers.java @@ -374,6 +374,10 @@ public UInt64 getMaxRequestBlocks() { return UInt64.valueOf(specConfig.getNetworkingConfig().getMaxRequestBlocks()); } + public boolean isFormerDepositMechanismDisabled(final BeaconState state) { + return false; + } + public Optional toVersionDeneb() { return Optional.empty(); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/attestation/AttestationWorthinessChecker.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/attestation/AttestationWorthinessChecker.java deleted file mode 100644 index becd6efcb91..00000000000 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/attestation/AttestationWorthinessChecker.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2022 - * - * 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.spec.logic.common.statetransition.attestation; - -import tech.pegasys.teku.spec.datastructures.operations.AttestationData; - -public interface AttestationWorthinessChecker { - AttestationWorthinessChecker NOOP = attestationGroup -> true; - - boolean areAttestationsWorthy(final AttestationData attestationData); -} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtil.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtil.java index 0fd5ef30d3b..39e1371b4b0 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtil.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtil.java @@ -38,6 +38,7 @@ import tech.pegasys.teku.spec.datastructures.attestation.ValidatableAttestation; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlockSummary; import tech.pegasys.teku.spec.datastructures.operations.Attestation; +import tech.pegasys.teku.spec.datastructures.operations.AttestationContainer; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation; import tech.pegasys.teku.spec.datastructures.operations.IndexedAttestation.IndexedAttestationSchema; @@ -47,7 +48,6 @@ import tech.pegasys.teku.spec.datastructures.util.AttestationProcessingResult; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; public abstract class AttestationUtil { @@ -99,8 +99,7 @@ public boolean isSlashableAttestationData( */ public IndexedAttestation getIndexedAttestation( final BeaconState state, final Attestation attestation) { - final List attestingIndices = - getAttestingIndices(state, attestation.getData(), attestation.getAggregationBits()); + final List attestingIndices = getAttestingIndices(state, attestation); final IndexedAttestationSchema indexedAttestationSchema = schemaDefinitions.getIndexedAttestationSchema(); @@ -117,28 +116,31 @@ public IndexedAttestation getIndexedAttestation( * Return the sorted attesting indices corresponding to ``data`` and ``bits``. * * @param state - * @param data - * @param bits + * @param attestation * @return * @throws IllegalArgumentException * @see - * https://github.com/ethereum/consensus-specs/blob/v0.8.0/specs/core/0_beacon-chain.md#get_attesting_indices + * https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#get_attesting_indices */ public IntList getAttestingIndices( - final BeaconState state, final AttestationData data, final SszBitlist bits) { - return IntList.of(streamAttestingIndices(state, data, bits).toArray()); + final BeaconState state, final AttestationContainer attestation) { + return IntList.of(streamAttestingIndices(state, attestation).toArray()); } public IntStream streamAttestingIndices( - final BeaconState state, final AttestationData data, final SszBitlist bits) { - IntList committee = + final BeaconState state, final AttestationContainer attestation) { + final AttestationData data = attestation.getData(); + final SszBitlist aggregationBits = attestation.getAggregationBits(); + final IntList committee = beaconStateAccessors.getBeaconCommittee(state, data.getSlot(), data.getIndex()); checkArgument( - bits.size() == committee.size(), + aggregationBits.size() == committee.size(), "Aggregation bitlist size (%s) does not match committee size (%s)", - bits.size(), + aggregationBits.size(), committee.size()); - return IntStream.range(0, committee.size()).filter(bits::getBit).map(committee::getInt); + return IntStream.range(0, committee.size()) + .filter(aggregationBits::getBit) + .map(committee::getInt); } public AttestationProcessingResult isValidIndexedAttestation( @@ -295,9 +297,6 @@ public AttestationData getGenericAttestationData( return new AttestationData(slot, committeeIndex, beaconBlockRoot, source, target); } - public abstract AttestationWorthinessChecker createAttestationWorthinessChecker( - BeaconState state); - public abstract Optional performSlotInclusionGossipValidation( Attestation attestation, UInt64 genesisTime, UInt64 currentTimeMillis); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/SpecLogicAltair.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/SpecLogicAltair.java index dd7e4ca73e5..62062a794d7 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/SpecLogicAltair.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/SpecLogicAltair.java @@ -35,10 +35,10 @@ import tech.pegasys.teku.spec.logic.versions.altair.helpers.MiscHelpersAltair; import tech.pegasys.teku.spec.logic.versions.altair.statetransition.epoch.EpochProcessorAltair; import tech.pegasys.teku.spec.logic.versions.altair.statetransition.epoch.ValidatorStatusFactoryAltair; -import tech.pegasys.teku.spec.logic.versions.altair.util.AttestationUtilAltair; import tech.pegasys.teku.spec.logic.versions.bellatrix.helpers.BellatrixTransitionHelpers; import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0; import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.OperationValidatorPhase0; +import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsAltair; public class SpecLogicAltair extends AbstractSpecLogic { @@ -105,7 +105,7 @@ public static SpecLogicAltair create( new BeaconStateUtil( config, schemaDefinitions, predicates, miscHelpers, beaconStateAccessors); final AttestationUtil attestationUtil = - new AttestationUtilAltair(config, schemaDefinitions, beaconStateAccessors, miscHelpers); + new AttestationUtilPhase0(config, schemaDefinitions, beaconStateAccessors, miscHelpers); // specific operation validator from spec so that things can hapepn like rejecting bls to // execution change final AttestationDataValidator attestationDataValidator = diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/forktransition/AltairStateUpgrade.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/forktransition/AltairStateUpgrade.java index 8e180c59e56..87d84395f8c 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/forktransition/AltairStateUpgrade.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/forktransition/AltairStateUpgrade.java @@ -102,7 +102,7 @@ private void translateParticipation( // Apply flags to all attesting validators final SszMutableList epochParticipation = state.getPreviousEpochParticipation(); attestationUtil - .streamAttestingIndices(state, data, attestation.getAggregationBits()) + .streamAttestingIndices(state, attestation) .forEach( index -> { final byte previousFlags = epochParticipation.get(index).get(); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/attestation/AttestationWorthinessCheckerAltair.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/attestation/AttestationWorthinessCheckerAltair.java deleted file mode 100644 index d917b63382b..00000000000 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/attestation/AttestationWorthinessCheckerAltair.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2022 - * - * 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.spec.logic.versions.altair.statetransition.attestation; - -import org.apache.tuweni.bytes.Bytes32; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.datastructures.operations.AttestationData; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; - -/** - * This filter is meant to exclude attestations that pay no rewards, thus not worthy to be included - * in a block - * - *

It currently filters out the attestations older than {@code - * integerSquareRoot(specVersion.getSlotsPerEpoch())} slots having the wrong target - */ -public class AttestationWorthinessCheckerAltair implements AttestationWorthinessChecker { - - private final Bytes32 expectedAttestationTarget; - private final UInt64 oldestWorthySlotForSourceReward; - - public AttestationWorthinessCheckerAltair( - final Bytes32 expectedAttestationTarget, final UInt64 oldestWorthySlotForSourceReward) { - this.expectedAttestationTarget = expectedAttestationTarget; - this.oldestWorthySlotForSourceReward = oldestWorthySlotForSourceReward; - } - - @Override - public boolean areAttestationsWorthy(final AttestationData attestationData) { - return attestationData.getSlot().isGreaterThanOrEqualTo(oldestWorthySlotForSourceReward) - || attestationData.getTarget().getRoot().equals(expectedAttestationTarget); - } -} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/util/AttestationUtilAltair.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/util/AttestationUtilAltair.java deleted file mode 100644 index f55671dac84..00000000000 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/util/AttestationUtilAltair.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2022 - * - * 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.spec.logic.versions.altair.util; - -import org.apache.tuweni.bytes.Bytes32; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.config.SpecConfig; -import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; -import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; -import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; -import tech.pegasys.teku.spec.logic.versions.altair.statetransition.attestation.AttestationWorthinessCheckerAltair; -import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0; -import tech.pegasys.teku.spec.schemas.SchemaDefinitions; - -public class AttestationUtilAltair extends AttestationUtilPhase0 { - public AttestationUtilAltair( - final SpecConfig specConfig, - final SchemaDefinitions schemaDefinitions, - final BeaconStateAccessors beaconStateAccessors, - final MiscHelpers miscHelpers) { - super(specConfig, schemaDefinitions, beaconStateAccessors, miscHelpers); - } - - @Override - public AttestationWorthinessChecker createAttestationWorthinessChecker(final BeaconState state) { - final UInt64 currentSlot = state.getSlot(); - final UInt64 startSlot = - miscHelpers.computeStartSlotAtEpoch(miscHelpers.computeEpochAtSlot(currentSlot)); - - final Bytes32 expectedAttestationTarget = - startSlot.compareTo(currentSlot) == 0 || currentSlot.compareTo(startSlot) <= 0 - ? state.getLatestBlockHeader().getRoot() - : beaconStateAccessors.getBlockRootAtSlot(state, startSlot); - - final UInt64 oldestWorthySlotForSourceReward = - state.getSlot().minusMinZero(specConfig.getSquareRootSlotsPerEpoch()); - return new AttestationWorthinessCheckerAltair( - expectedAttestationTarget, oldestWorthySlotForSourceReward); - } -} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/bellatrix/SpecLogicBellatrix.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/bellatrix/SpecLogicBellatrix.java index 0744a1fea6f..0db09a51ee8 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/bellatrix/SpecLogicBellatrix.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/bellatrix/SpecLogicBellatrix.java @@ -30,7 +30,6 @@ import tech.pegasys.teku.spec.logic.common.util.ValidatorsUtil; import tech.pegasys.teku.spec.logic.versions.altair.helpers.BeaconStateAccessorsAltair; import tech.pegasys.teku.spec.logic.versions.altair.statetransition.epoch.ValidatorStatusFactoryAltair; -import tech.pegasys.teku.spec.logic.versions.altair.util.AttestationUtilAltair; import tech.pegasys.teku.spec.logic.versions.bellatrix.block.BlockProcessorBellatrix; import tech.pegasys.teku.spec.logic.versions.bellatrix.forktransition.BellatrixStateUpgrade; import tech.pegasys.teku.spec.logic.versions.bellatrix.helpers.BeaconStateMutatorsBellatrix; @@ -40,6 +39,7 @@ import tech.pegasys.teku.spec.logic.versions.bellatrix.util.BlindBlockUtilBellatrix; import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0; import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.OperationValidatorPhase0; +import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix; public class SpecLogicBellatrix extends AbstractSpecLogic { @@ -111,7 +111,7 @@ public static SpecLogicBellatrix create( new BeaconStateUtil( config, schemaDefinitions, predicates, miscHelpers, beaconStateAccessors); final AttestationUtil attestationUtil = - new AttestationUtilAltair(config, schemaDefinitions, beaconStateAccessors, miscHelpers); + new AttestationUtilPhase0(config, schemaDefinitions, beaconStateAccessors, miscHelpers); final AttestationDataValidator attestationDataValidator = new AttestationDataValidatorPhase0(config, miscHelpers, beaconStateAccessors); final OperationValidator operationValidator = diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/SpecLogicCapella.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/SpecLogicCapella.java index 8d8909534ad..c69086e2500 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/SpecLogicCapella.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/SpecLogicCapella.java @@ -30,7 +30,6 @@ import tech.pegasys.teku.spec.logic.common.util.ValidatorsUtil; import tech.pegasys.teku.spec.logic.versions.altair.helpers.BeaconStateAccessorsAltair; import tech.pegasys.teku.spec.logic.versions.altair.statetransition.epoch.ValidatorStatusFactoryAltair; -import tech.pegasys.teku.spec.logic.versions.altair.util.AttestationUtilAltair; import tech.pegasys.teku.spec.logic.versions.bellatrix.helpers.BeaconStateMutatorsBellatrix; import tech.pegasys.teku.spec.logic.versions.bellatrix.helpers.BellatrixTransitionHelpers; import tech.pegasys.teku.spec.logic.versions.bellatrix.statetransition.epoch.EpochProcessorBellatrix; @@ -41,6 +40,7 @@ import tech.pegasys.teku.spec.logic.versions.capella.operations.validation.OperationValidatorCapella; import tech.pegasys.teku.spec.logic.versions.capella.statetransition.epoch.EpochProcessorCapella; import tech.pegasys.teku.spec.logic.versions.phase0.operations.validation.AttestationDataValidatorPhase0; +import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsCapella; public class SpecLogicCapella extends AbstractSpecLogic { @@ -111,7 +111,7 @@ public static SpecLogicCapella create( new BeaconStateUtil( config, schemaDefinitions, predicates, miscHelpers, beaconStateAccessors); final AttestationUtil attestationUtil = - new AttestationUtilAltair(config, schemaDefinitions, beaconStateAccessors, miscHelpers); + new AttestationUtilPhase0(config, schemaDefinitions, beaconStateAccessors, miscHelpers); final AttestationDataValidator attestationDataValidator = new AttestationDataValidatorPhase0(config, miscHelpers, beaconStateAccessors); final OperationValidator operationValidator = diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapella.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapella.java index c45aeff4e51..36b6fc4124c 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapella.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapella.java @@ -139,13 +139,15 @@ protected void processOperationsNoValidation( throws BlockProcessingException { super.processOperationsNoValidation(state, body, indexedAttestationCache); - processBlsToExecutionChangesNoValidation( - MutableBeaconStateCapella.required(state), - body.getOptionalBlsToExecutionChanges() - .orElseThrow( - () -> - new BlockProcessingException( - "BlsToExecutionChanges was not found during block processing."))); + safelyProcess( + () -> + processBlsToExecutionChangesNoValidation( + MutableBeaconStateCapella.required(state), + body.getOptionalBlsToExecutionChanges() + .orElseThrow( + () -> + new BlockProcessingException( + "BlsToExecutionChanges was not found during block processing.")))); } @Override diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/util/AttestationUtilDeneb.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/util/AttestationUtilDeneb.java index ab31abc8449..535faec3146 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/util/AttestationUtilDeneb.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/deneb/util/AttestationUtilDeneb.java @@ -23,10 +23,10 @@ import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; -import tech.pegasys.teku.spec.logic.versions.altair.util.AttestationUtilAltair; +import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; -public class AttestationUtilDeneb extends AttestationUtilAltair { +public class AttestationUtilDeneb extends AttestationUtilPhase0 { public AttestationUtilDeneb( final SpecConfig specConfig, diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/SpecLogicElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/SpecLogicElectra.java index eda0f40c05f..bd8f8ee8e39 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/SpecLogicElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/SpecLogicElectra.java @@ -36,13 +36,14 @@ import tech.pegasys.teku.spec.logic.versions.capella.block.BlockProcessorCapella; import tech.pegasys.teku.spec.logic.versions.capella.operations.validation.OperationValidatorCapella; import tech.pegasys.teku.spec.logic.versions.capella.statetransition.epoch.EpochProcessorCapella; -import tech.pegasys.teku.spec.logic.versions.deneb.block.BlockProcessorDeneb; import tech.pegasys.teku.spec.logic.versions.deneb.helpers.BeaconStateAccessorsDeneb; import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; import tech.pegasys.teku.spec.logic.versions.deneb.operations.validation.AttestationDataValidatorDeneb; import tech.pegasys.teku.spec.logic.versions.deneb.util.AttestationUtilDeneb; import tech.pegasys.teku.spec.logic.versions.deneb.util.ForkChoiceUtilDeneb; +import tech.pegasys.teku.spec.logic.versions.electra.block.BlockProcessorElectra; import tech.pegasys.teku.spec.logic.versions.electra.forktransition.ElectraStateUpgrade; +import tech.pegasys.teku.spec.logic.versions.electra.helpers.MiscHelpersElectra; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; public class SpecLogicElectra extends AbstractSpecLogic { @@ -93,8 +94,8 @@ public static SpecLogicElectra create( final SpecConfigElectra config, final SchemaDefinitionsElectra schemaDefinitions) { // Helpers final Predicates predicates = new Predicates(config); - final MiscHelpersDeneb miscHelpers = - new MiscHelpersDeneb(config, predicates, schemaDefinitions); + final MiscHelpersElectra miscHelpers = + new MiscHelpersElectra(config, predicates, schemaDefinitions); final BeaconStateAccessorsDeneb beaconStateAccessors = new BeaconStateAccessorsDeneb(config, predicates, miscHelpers); final BeaconStateMutatorsBellatrix beaconStateMutators = @@ -140,8 +141,8 @@ public static SpecLogicElectra create( beaconStateAccessors, validatorsUtil, config, miscHelpers, schemaDefinitions); final LightClientUtil lightClientUtil = new LightClientUtil(beaconStateAccessors, syncCommitteeUtil, schemaDefinitions); - final BlockProcessorDeneb blockProcessor = - new BlockProcessorDeneb( + final BlockProcessorElectra blockProcessor = + new BlockProcessorElectra( config, predicates, miscHelpers, diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/block/BlockProcessorElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/block/BlockProcessorElectra.java new file mode 100644 index 00000000000..72972137565 --- /dev/null +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/block/BlockProcessorElectra.java @@ -0,0 +1,141 @@ +/* + * Copyright Consensys Software Inc., 2022 + * + * 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.spec.logic.versions.electra.block; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Optional; +import tech.pegasys.teku.infrastructure.ssz.SszList; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.cache.IndexedAttestationCache; +import tech.pegasys.teku.spec.config.SpecConfigElectra; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; +import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionPayloadElectra; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.MutableBeaconState; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; +import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators; +import tech.pegasys.teku.spec.logic.common.helpers.Predicates; +import tech.pegasys.teku.spec.logic.common.operations.OperationSignatureVerifier; +import tech.pegasys.teku.spec.logic.common.operations.validation.OperationValidator; +import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.BlockProcessingException; +import tech.pegasys.teku.spec.logic.common.util.AttestationUtil; +import tech.pegasys.teku.spec.logic.common.util.BeaconStateUtil; +import tech.pegasys.teku.spec.logic.common.util.SyncCommitteeUtil; +import tech.pegasys.teku.spec.logic.common.util.ValidatorsUtil; +import tech.pegasys.teku.spec.logic.versions.altair.helpers.BeaconStateAccessorsAltair; +import tech.pegasys.teku.spec.logic.versions.deneb.block.BlockProcessorDeneb; +import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsDeneb; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; + +public class BlockProcessorElectra extends BlockProcessorDeneb { + + public BlockProcessorElectra( + final SpecConfigElectra specConfig, + final Predicates predicates, + final MiscHelpersDeneb miscHelpers, + final SyncCommitteeUtil syncCommitteeUtil, + final BeaconStateAccessorsAltair beaconStateAccessors, + final BeaconStateMutators beaconStateMutators, + final OperationSignatureVerifier operationSignatureVerifier, + final BeaconStateUtil beaconStateUtil, + final AttestationUtil attestationUtil, + final ValidatorsUtil validatorsUtil, + final OperationValidator operationValidator, + final SchemaDefinitionsElectra schemaDefinitions) { + super( + specConfig, + predicates, + miscHelpers, + syncCommitteeUtil, + beaconStateAccessors, + beaconStateMutators, + operationSignatureVerifier, + beaconStateUtil, + attestationUtil, + validatorsUtil, + operationValidator, + SchemaDefinitionsDeneb.required(schemaDefinitions)); + } + + @Override + protected void processOperationsNoValidation( + final MutableBeaconState state, + final BeaconBlockBody body, + final IndexedAttestationCache indexedAttestationCache) + throws BlockProcessingException { + super.processOperationsNoValidation(state, body, indexedAttestationCache); + + safelyProcess( + () -> + processDepositReceipts( + MutableBeaconStateElectra.required(state), + body.getOptionalExecutionPayload() + .flatMap(ExecutionPayload::toVersionElectra) + .map(ExecutionPayloadElectra::getDepositReceipts) + .orElseThrow( + () -> + new BlockProcessingException( + "Deposit receipts were not found during block processing.")))); + } + + @Override + protected void verifyOutstandingDepositsAreProcessed( + final BeaconState state, final BeaconBlockBody body) { + final UInt64 eth1DepositIndexLimit = + state + .getEth1Data() + .getDepositCount() + .min(BeaconStateElectra.required(state).getDepositReceiptsStartIndex()); + + if (state.getEth1DepositIndex().isLessThan(eth1DepositIndexLimit)) { + final int expectedDepositCount = + Math.min( + specConfig.getMaxDeposits(), + eth1DepositIndexLimit.minus(state.getEth1DepositIndex()).intValue()); + + checkArgument( + body.getDeposits().size() == expectedDepositCount, + "process_operations: Verify that outstanding deposits are processed up to the maximum number of deposits"); + } else { + checkArgument( + body.getDeposits().isEmpty(), + "process_operations: Verify that former deposit mechanism has been disabled"); + } + } + + public void processDepositReceipts( + final MutableBeaconStateElectra state, final SszList depositReceipts) { + for (DepositReceipt depositReceipt : depositReceipts) { + // process_deposit_receipt + if (state + .getDepositReceiptsStartIndex() + .equals(SpecConfigElectra.UNSET_DEPOSIT_RECEIPTS_START_INDEX)) { + state.setDepositReceiptsStartIndex(depositReceipt.getIndex()); + } + applyDeposit( + state, + depositReceipt.getPubkey(), + depositReceipt.getWithdrawalCredentials(), + depositReceipt.getAmount(), + depositReceipt.getSignature(), + Optional.empty(), + false); + } + } +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java new file mode 100644 index 00000000000..76e2148a920 --- /dev/null +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectra.java @@ -0,0 +1,41 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.spec.logic.versions.electra.helpers; + +import tech.pegasys.teku.spec.config.SpecConfigElectra; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; +import tech.pegasys.teku.spec.logic.common.helpers.Predicates; +import tech.pegasys.teku.spec.logic.versions.deneb.helpers.MiscHelpersDeneb; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; + +public class MiscHelpersElectra extends MiscHelpersDeneb { + + public MiscHelpersElectra( + final SpecConfigElectra specConfig, + final Predicates predicates, + final SchemaDefinitionsElectra schemaDefinitions) { + super(specConfig, predicates, schemaDefinitions); + } + + @Override + public boolean isFormerDepositMechanismDisabled(final BeaconState state) { + // if the next deposit to be processed by Eth1Data poll has the index of the first deposit + // processed with the new deposit flow, i.e. `eth1_deposit_index == + // deposit_receipts_start_index`, we should stop Eth1Data deposits processing + return state + .getEth1DepositIndex() + .equals(BeaconStateElectra.required(state).getDepositReceiptsStartIndex()); + } +} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/ValidatorStatusFactoryPhase0.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/ValidatorStatusFactoryPhase0.java index 84fd99c43b6..bc22f19b7ca 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/ValidatorStatusFactoryPhase0.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/ValidatorStatusFactoryPhase0.java @@ -96,7 +96,7 @@ protected void processParticipation( // Apply flags to attestingIndices attestationUtil - .streamAttestingIndices(state, data, attestation.getAggregationBits()) + .streamAttestingIndices(state, attestation) .mapToObj(statuses::get) .forEach(updates::apply); }); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/util/AttestationUtilPhase0.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/util/AttestationUtilPhase0.java index 1daa8626b91..7d850e7691c 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/util/AttestationUtilPhase0.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/util/AttestationUtilPhase0.java @@ -22,10 +22,8 @@ import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; import tech.pegasys.teku.spec.datastructures.operations.Attestation; -import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; import tech.pegasys.teku.spec.logic.common.util.AttestationUtil; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; @@ -41,11 +39,6 @@ public AttestationUtilPhase0( super(specConfig, schemaDefinitions, beaconStateAccessors, miscHelpers); } - @Override - public AttestationWorthinessChecker createAttestationWorthinessChecker(final BeaconState state) { - return AttestationWorthinessChecker.NOOP; - } - /** * [IGNORE] attestation.data.slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots * (with a MAXIMUM_GOSSIP_CLOCK_DISPARITY allowance) -- i.e. attestation.data.slot + diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/electra.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/electra.yaml index 5ee402dd796..b920664fff2 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/electra.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/electra.yaml @@ -1,11 +1,8 @@ # Mainnet preset - Electra -# Max operations per block -# --------------------------------------------------------------- -# 2**4 (= 16) -MAX_EXECUTION_LAYER_EXITS: 16 - # Execution # --------------------------------------------------------------- # 2**13 (= 8192) receipts -MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192 \ No newline at end of file +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 8192 +# 2**4 (= 16) exits +MAX_EXECUTION_LAYER_EXITS: 16 \ No newline at end of file diff --git a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/minimal/electra.yaml b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/minimal/electra.yaml index d8a8b77dd38..ff5bd201834 100644 --- a/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/minimal/electra.yaml +++ b/ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/minimal/electra.yaml @@ -1,11 +1,8 @@ # Minimal preset - Electra -# Max operations per block -# --------------------------------------------------------------- -# 2**4 (= 16) -MAX_EXECUTION_LAYER_EXITS: 16 - # Execution # --------------------------------------------------------------- # [customized] -MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4 \ No newline at end of file +MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: 4 +# 2**4 (= 16) exits +MAX_EXECUTION_LAYER_EXITS: 16 \ No newline at end of file diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecFactoryTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecFactoryTest.java deleted file mode 100644 index d5cb3fba981..00000000000 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/SpecFactoryTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2022 - * - * 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.spec; - -import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.teku.spec.SpecMilestone.ALTAIR; -import static tech.pegasys.teku.spec.SpecMilestone.BELLATRIX; -import static tech.pegasys.teku.spec.SpecMilestone.CAPELLA; -import static tech.pegasys.teku.spec.SpecMilestone.DENEB; -import static tech.pegasys.teku.spec.SpecMilestone.PHASE0; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Stream; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.EnumSource; -import org.junit.jupiter.params.provider.MethodSource; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.config.SpecConfig; -import tech.pegasys.teku.spec.config.SpecConfigLoader; -import tech.pegasys.teku.spec.logic.versions.altair.statetransition.attestation.AttestationWorthinessCheckerAltair; -import tech.pegasys.teku.spec.networks.Eth2Network; -import tech.pegasys.teku.spec.util.DataStructureUtil; - -public class SpecFactoryTest { - - private static final Set NON_BELLATRIX_NETWORKS = Set.of("swift", "less-swift"); - - private static final Set CAPELLA_NETWORKS = Set.of("lukso"); - - private static final Set DENEB_NETWORKS = - Set.of("prater", "gnosis", "chiado", "sepolia", "holesky", "mainnet"); - - @Test - public void defaultFactoryShouldScheduleBellatrixCapellaAndDenebForMainNet() { - final Spec spec = SpecFactory.create("mainnet"); - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .containsExactly(PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("getKnownConfigNames") - public void defaultFactoryShouldNotEnableBellatrixUnlessForkEpochIsSet(final String configName) { - final Spec spec = SpecFactory.create(configName); - if (configName.equals("minimal")) { // Minimal doesn't have altair scheduled - assertThat(spec.getForkSchedule().getSupportedMilestones()).containsExactly(PHASE0); - } else if (NON_BELLATRIX_NETWORKS.contains(configName)) { - assertThat(spec.getForkSchedule().getSupportedMilestones()).containsExactly(PHASE0, ALTAIR); - } else if (CAPELLA_NETWORKS.contains(configName)) { - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .containsExactly(PHASE0, ALTAIR, BELLATRIX, CAPELLA); - } else if (DENEB_NETWORKS.contains(configName)) { - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .containsExactly(PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB); - } else { - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .containsExactly(PHASE0, ALTAIR, BELLATRIX); - } - } - - @ParameterizedTest(name = "{0}") - @MethodSource("getKnownConfigNames") - public void shouldSupportCapellaWhenForkEpochSetInConfig(final String configName) { - final Spec spec = SpecFactory.create(configName); - if (CAPELLA_NETWORKS.contains(configName)) { - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .containsExactly(PHASE0, ALTAIR, BELLATRIX, CAPELLA); - } else if (DENEB_NETWORKS.contains(configName)) { - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .contains(PHASE0, ALTAIR, BELLATRIX, CAPELLA); - } else { - assertThat(spec.getForkSchedule().getSupportedMilestones()).doesNotContain(CAPELLA); - } - } - - @ParameterizedTest(name = "{0}") - @MethodSource("getKnownConfigNames") - public void shouldSupportDenebWhenForkEpochSetInConfig(final String configName) { - final Spec spec = SpecFactory.create(configName); - if (DENEB_NETWORKS.contains(configName)) { - assertThat(spec.getForkSchedule().getSupportedMilestones()) - .containsExactly(PHASE0, ALTAIR, BELLATRIX, CAPELLA, DENEB); - } else { - assertThat(spec.getForkSchedule().getSupportedMilestones()).doesNotContain(DENEB); - } - } - - @Test - void shouldSupportAltairWhenForkEpochSetInConfig() { - final SpecConfig config = - SpecConfigLoader.loadConfig( - "minimal", - phase0Builder -> - phase0Builder.altairBuilder( - altairBuilder -> altairBuilder.altairForkEpoch(UInt64.valueOf(10)))); - final Spec spec = SpecFactory.create(config); - assertThat(spec.getForkSchedule().getSupportedMilestones()).containsExactly(PHASE0, ALTAIR); - assertThat(spec.getForkSchedule().getSpecMilestoneAtEpoch(UInt64.valueOf(10))) - .isEqualTo(ALTAIR); - } - - @SuppressWarnings("unused") - static Stream getKnownConfigNames() { - return Arrays.stream(Eth2Network.values()).map(Eth2Network::configName).map(Arguments::of); - } - - @ParameterizedTest - @EnumSource(value = SpecMilestone.class) - public void shouldCreateTheRightAttestationWorthinessChecker(SpecMilestone milestone) { - final Spec spec = TestSpecFactory.createMainnet(milestone); - final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); - - if (milestone.isGreaterThanOrEqualTo(ALTAIR)) { - assertThat(spec.createAttestationWorthinessChecker(dataStructureUtil.randomBeaconState())) - .isInstanceOf(AttestationWorthinessCheckerAltair.class); - } else { - assertThat(spec.createAttestationWorthinessChecker(dataStructureUtil.randomBeaconState())) - .isInstanceOf(AttestationWorthinessCheckerAltair.NOOP.getClass()); - } - } -} diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpersTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpersTest.java index 65ac51aa561..66bce5ee02a 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpersTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/helpers/MiscHelpersTest.java @@ -34,8 +34,10 @@ import org.junit.jupiter.params.provider.MethodSource; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.config.SpecConfig; +import tech.pegasys.teku.spec.networks.Eth2Network; import tech.pegasys.teku.spec.util.DataStructureUtil; class MiscHelpersTest { @@ -231,6 +233,20 @@ public void committeeComputationShouldNotOverflow(int activeValidatorsCount, int }); } + @Test + public void isFormerDepositReceiptMechanismDisabled_returnsFalseForAllForksPriorToElectra() { + SpecMilestone.getAllPriorMilestones(SpecMilestone.ELECTRA) + .forEach( + milestone -> { + final Spec spec = TestSpecFactory.create(milestone, Eth2Network.MINIMAL); + final MiscHelpers miscHelpers = spec.atSlot(UInt64.ZERO).miscHelpers(); + assertThat( + miscHelpers.isFormerDepositMechanismDisabled( + dataStructureUtil.randomBeaconState())) + .isFalse(); + }); + } + public static Stream getComputesSlotAtTimeArguments() { // 6 seconds per slot return Stream.of( diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtilTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtilTest.java index 9e07562139f..255470c5d84 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtilTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/common/util/AttestationUtilTest.java @@ -31,7 +31,6 @@ import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.ssz.Merkleizable; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.SpecVersion; @@ -43,13 +42,10 @@ import tech.pegasys.teku.spec.datastructures.util.AttestationProcessingResult; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; -import tech.pegasys.teku.spec.logic.versions.altair.statetransition.attestation.AttestationWorthinessCheckerAltair; -import tech.pegasys.teku.spec.logic.versions.altair.util.AttestationUtilAltair; import tech.pegasys.teku.spec.logic.versions.phase0.util.AttestationUtilPhase0; import tech.pegasys.teku.spec.util.DataStructureUtil; -@TestSpecContext(milestone = {SpecMilestone.PHASE0, SpecMilestone.ALTAIR}) +@TestSpecContext(milestone = {SpecMilestone.PHASE0}) class AttestationUtilTest { private final MiscHelpers miscHelpers = mock(MiscHelpers.class); @@ -88,36 +84,6 @@ void setUp(final SpecContext specContext) { beaconStateAccessors, miscHelpers); break; - case ALTAIR: - attestationUtil = - new AttestationUtilAltair( - spec.getGenesisSpecConfig(), - specVersion.getSchemaDefinitions(), - beaconStateAccessors, - miscHelpers); - break; - default: - throw new UnsupportedOperationException("unsupported milestone"); - } - } - - @TestTemplate - void shouldCreateAttestationWorthinessChecker(final SpecContext specContext) { - when(miscHelpers.computeStartSlotAtEpoch(any())).thenReturn(UInt64.ONE); - - switch (specContext.getSpecMilestone()) { - case PHASE0: - assertThat( - attestationUtil.createAttestationWorthinessChecker( - dataStructureUtil.randomBeaconState(UInt64.ONE))) - .isEqualTo(AttestationWorthinessChecker.NOOP); - break; - case ALTAIR: - assertThat( - attestationUtil.createAttestationWorthinessChecker( - dataStructureUtil.randomBeaconState(UInt64.ONE))) - .isInstanceOf(AttestationWorthinessCheckerAltair.class); - break; default: throw new UnsupportedOperationException("unsupported milestone"); } diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/attestation/AttestationWorthinessCheckerAltairTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/attestation/AttestationWorthinessCheckerAltairTest.java deleted file mode 100644 index feeee9f296c..00000000000 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/attestation/AttestationWorthinessCheckerAltairTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2022 - * - * 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.spec.logic.versions.altair.statetransition.attestation; - -import static org.assertj.core.api.Assertions.assertThat; -import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; - -import org.junit.jupiter.api.Test; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.TestSpecFactory; -import tech.pegasys.teku.spec.datastructures.operations.AttestationData; -import tech.pegasys.teku.spec.datastructures.state.Checkpoint; -import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; -import tech.pegasys.teku.spec.util.DataStructureUtil; - -public class AttestationWorthinessCheckerAltairTest { - - final Spec spec = TestSpecFactory.createMainnetAltair(); - final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); - final AttestationData referenceAttestationData = - dataStructureUtil.randomAttestationData(UInt64.valueOf(1)); - final BeaconState state = dataStructureUtil.randomBeaconState(UInt64.valueOf(10)); - final Checkpoint correctTarget = - new Checkpoint( - dataStructureUtil.randomEpoch(), - spec.atSlot(state.getSlot()).beaconStateAccessors().getBlockRootAtSlot(state, ZERO)); - final Checkpoint wrongTarget = dataStructureUtil.randomCheckpoint(); - - @Test - void shouldIncludeSlotOldTargetOK() { - AttestationData attestation = generateAttestationData(UInt64.valueOf(1), correctTarget); - - assertThat(spec.createAttestationWorthinessChecker(state).areAttestationsWorthy(attestation)) - .isTrue(); - } - - @Test - void shouldIncludeSlotOldTargetWrong() { - AttestationData attestation = generateAttestationData(UInt64.valueOf(3), wrongTarget); - - assertThat(spec.createAttestationWorthinessChecker(state).areAttestationsWorthy(attestation)) - .isFalse(); - } - - @Test - void shouldIncludeSlotOKTargetWrong() { - AttestationData attestation = generateAttestationData(UInt64.valueOf(5), wrongTarget); - - assertThat(spec.createAttestationWorthinessChecker(state).areAttestationsWorthy(attestation)) - .isTrue(); - } - - @Test - void shouldIncludeSlotOKTargetOK() { - AttestationData attestation = generateAttestationData(UInt64.valueOf(7), correctTarget); - - assertThat(spec.createAttestationWorthinessChecker(state).areAttestationsWorthy(attestation)) - .isTrue(); - } - - @Test - void shouldIncludeCloseToGenesis() { - final BeaconState closeToGenesisState = dataStructureUtil.randomBeaconState(UInt64.valueOf(2)); - - AttestationData attestation = generateAttestationData(UInt64.valueOf(1), wrongTarget); - - assertThat( - spec.createAttestationWorthinessChecker(closeToGenesisState) - .areAttestationsWorthy(attestation)) - .isTrue(); - } - - private AttestationData generateAttestationData(UInt64 slot, Checkpoint target) { - return new AttestationData( - slot, - referenceAttestationData.getIndex(), - referenceAttestationData.getBeaconBlockRoot(), - referenceAttestationData.getSource(), - target); - } -} diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/bellatrix/block/BlockProcessorBellatrixTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/bellatrix/block/BlockProcessorBellatrixTest.java index 059c91bb7b8..78c47203003 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/bellatrix/block/BlockProcessorBellatrixTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/bellatrix/block/BlockProcessorBellatrixTest.java @@ -14,38 +14,24 @@ package tech.pegasys.teku.spec.logic.versions.bellatrix.block; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import java.util.Optional; import org.junit.jupiter.api.Test; -import tech.pegasys.teku.bls.BLSSignatureVerifier; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; -import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; import tech.pegasys.teku.spec.datastructures.execution.NewPayloadRequest; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.BlockProcessingException; -import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.StateTransitionException; import tech.pegasys.teku.spec.logic.versions.altair.block.BlockProcessorAltairTest; public class BlockProcessorBellatrixTest extends BlockProcessorAltairTest { + @Override protected Spec createSpec() { return TestSpecFactory.createMainnetBellatrix(); } - @Test - void shouldRejectAltairBlock() { - final BeaconState preState = createBeaconState(); - final SignedBeaconBlock block = - dataStructureUtil.randomSignedBeaconBlock(preState.getSlot().increment()); - assertThatThrownBy( - () -> spec.processBlock(preState, block, BLSSignatureVerifier.SIMPLE, Optional.empty())) - .isInstanceOf(StateTransitionException.class); - } - @Test public void shouldCreateNewPayloadRequest() throws BlockProcessingException { final BeaconState preState = createBeaconState(); diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapellaTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapellaTest.java index c8156d23737..909bd617dc3 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapellaTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/capella/block/BlockProcessorCapellaTest.java @@ -14,7 +14,6 @@ package tech.pegasys.teku.spec.logic.versions.capella.block; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import java.util.ArrayList; import java.util.List; @@ -29,7 +28,6 @@ import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.config.SpecConfig; -import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.execution.versions.capella.Withdrawal; import tech.pegasys.teku.spec.datastructures.operations.BlsToExecutionChange; import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange; @@ -38,7 +36,6 @@ import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.generator.ChainBuilder; import tech.pegasys.teku.spec.logic.common.statetransition.blockvalidator.BlockValidationResult; -import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.StateTransitionException; import tech.pegasys.teku.spec.logic.versions.bellatrix.block.BlockProcessorBellatrixTest; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsCapella; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -50,16 +47,6 @@ protected Spec createSpec() { return TestSpecFactory.createMainnetCapella(); } - @Test - void shouldRejectBellatrixBlock() { - BeaconState preState = createBeaconState(); - final SignedBeaconBlock block = - dataStructureUtil.randomSignedBeaconBlock(preState.getSlot().increment()); - assertThatThrownBy( - () -> spec.processBlock(preState, block, BLSSignatureVerifier.SIMPLE, Optional.empty())) - .isInstanceOf(StateTransitionException.class); - } - @Test void shouldCreateExpectedWithdrawalAddress() { Bytes20 eth1Address = dataStructureUtil.randomBytes20(); diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/block/BlockProcessorDenebTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/block/BlockProcessorDenebTest.java index 072ba7e6873..6dd2c4cbfe7 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/block/BlockProcessorDenebTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/deneb/block/BlockProcessorDenebTest.java @@ -20,38 +20,26 @@ import java.util.Optional; import java.util.stream.Collectors; import org.junit.jupiter.api.Test; -import tech.pegasys.teku.bls.BLSSignatureVerifier; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.config.SpecConfigDeneb; -import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; import tech.pegasys.teku.spec.datastructures.execution.NewPayloadRequest; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.BlockProcessingException; -import tech.pegasys.teku.spec.logic.common.statetransition.exceptions.StateTransitionException; import tech.pegasys.teku.spec.logic.versions.capella.block.BlockProcessorCapellaTest; import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; public class BlockProcessorDenebTest extends BlockProcessorCapellaTest { + @Override protected Spec createSpec() { return TestSpecFactory.createMainnetDeneb(); } - @Test - void shouldRejectCapellaBlock() { - BeaconState preState = createBeaconState(); - final SignedBeaconBlock block = - dataStructureUtil.randomSignedBeaconBlock(preState.getSlot().increment()); - assertThatThrownBy( - () -> spec.processBlock(preState, block, BLSSignatureVerifier.SIMPLE, Optional.empty())) - .isInstanceOf(StateTransitionException.class); - } - @Test void shouldFailProcessingIfCommitmentsInBlockAreMoreThanMaxBlobsPerBlock() { final BeaconState preState = createBeaconState(); diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/block/BlockProcessorElectraTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/block/BlockProcessorElectraTest.java new file mode 100644 index 00000000000..2336ef6c09c --- /dev/null +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/block/BlockProcessorElectraTest.java @@ -0,0 +1,143 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.spec.logic.versions.electra.block; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import java.util.stream.IntStream; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.infrastructure.ssz.SszList; +import tech.pegasys.teku.infrastructure.ssz.schema.SszListSchema; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.config.SpecConfigElectra; +import tech.pegasys.teku.spec.datastructures.blocks.Eth1Data; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; +import tech.pegasys.teku.spec.datastructures.execution.versions.electra.DepositReceipt; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; +import tech.pegasys.teku.spec.logic.versions.deneb.block.BlockProcessorDenebTest; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; + +class BlockProcessorElectraTest extends BlockProcessorDenebTest { + + @Override + protected Spec createSpec() { + return TestSpecFactory.createMainnetElectra(); + } + + @Test + public void verifiesOutstandingEth1DepositsAreProcessed() { + final BeaconState state = + createBeaconState() + .updated( + mutableState -> { + final UInt64 eth1DepositCount = UInt64.valueOf(25); + mutableState.setEth1Data( + new Eth1Data( + dataStructureUtil.randomBytes32(), + eth1DepositCount, + dataStructureUtil.randomBytes32())); + final UInt64 eth1DepositIndex = UInt64.valueOf(13); + mutableState.setEth1DepositIndex(eth1DepositIndex); + final UInt64 depositReceiptsStartIndex = UInt64.valueOf(20); + MutableBeaconStateElectra.required(mutableState) + .setDepositReceiptsStartIndex(depositReceiptsStartIndex); + }); + + final BeaconBlockBody body = + dataStructureUtil.randomBeaconBlockBody( + // 20 - 13 = 7 + builder -> builder.deposits(dataStructureUtil.randomSszDeposits(7))); + + getBlockProcessor(state).verifyOutstandingDepositsAreProcessed(state, body); + } + + @Test + public void + verifiesNoOutstandingEth1DepositsAreProcessedWhenFormerDepositMechanismHasBeenDisabled() { + final BeaconState state = + createBeaconState() + .updated( + mutableState -> { + final UInt64 eth1DepositCount = UInt64.valueOf(25); + mutableState.setEth1Data( + new Eth1Data( + dataStructureUtil.randomBytes32(), + eth1DepositCount, + dataStructureUtil.randomBytes32())); + final UInt64 eth1DepositIndex = UInt64.valueOf(20); + mutableState.setEth1DepositIndex(eth1DepositIndex); + final UInt64 depositReceiptsStartIndex = UInt64.valueOf(20); + MutableBeaconStateElectra.required(mutableState) + .setDepositReceiptsStartIndex(depositReceiptsStartIndex); + }); + + final BeaconBlockBody body = + dataStructureUtil.randomBeaconBlockBody( + // 20 - 20 = 0 + modifier -> modifier.deposits(dataStructureUtil.randomSszDeposits(0))); + + getBlockProcessor(state).verifyOutstandingDepositsAreProcessed(state, body); + } + + @Test + public void processesDepositReceipts() { + final BeaconStateElectra preState = + BeaconStateElectra.required( + createBeaconState() + .updated( + mutableState -> + MutableBeaconStateElectra.required(mutableState) + .setDepositReceiptsStartIndex( + SpecConfigElectra.UNSET_DEPOSIT_RECEIPTS_START_INDEX))); + final int firstElectraDepositReceiptIndex = preState.getValidators().size(); + + final SszListSchema> depositReceiptsSchema = + SchemaDefinitionsElectra.required(spec.getGenesisSchemaDefinitions()) + .getExecutionPayloadSchema() + .getDepositReceiptsSchemaRequired(); + final int depositReceiptsCount = 3; + final List depositReceipts = + IntStream.range(0, depositReceiptsCount) + .mapToObj( + i -> + dataStructureUtil.randomDepositReceiptWithValidSignature( + UInt64.valueOf(firstElectraDepositReceiptIndex + i))) + .toList(); + + final BeaconStateElectra state = + BeaconStateElectra.required( + preState.updated( + mutableState -> + getBlockProcessor(preState) + .processDepositReceipts( + MutableBeaconStateElectra.required(mutableState), + depositReceiptsSchema.createFromElements(depositReceipts)))); + + // verify deposit_receipts_start_index has been set + assertThat(state.getDepositReceiptsStartIndex()) + .isEqualTo(UInt64.valueOf(firstElectraDepositReceiptIndex)); + // verify validators have been added to the state + assertThat(state.getValidators().size()) + .isEqualTo(firstElectraDepositReceiptIndex + depositReceiptsCount); + } + + private BlockProcessorElectra getBlockProcessor(final BeaconState state) { + return (BlockProcessorElectra) spec.getBlockProcessor(state.getSlot()); + } +} diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java new file mode 100644 index 00000000000..5a3698f1719 --- /dev/null +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/MiscHelpersElectraTest.java @@ -0,0 +1,76 @@ +/* + * Copyright Consensys Software Inc., 2024 + * + * 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.spec.logic.versions.electra.helpers; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.config.SpecConfigElectra; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; +import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; +import tech.pegasys.teku.spec.logic.common.helpers.Predicates; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; +import tech.pegasys.teku.spec.util.DataStructureUtil; + +public class MiscHelpersElectraTest { + + private final Spec spec = TestSpecFactory.createMinimalElectra(); + private final Predicates predicates = new Predicates(spec.getGenesisSpecConfig()); + private final SchemaDefinitionsElectra schemaDefinitionsElectra = + SchemaDefinitionsElectra.required(spec.getGenesisSchemaDefinitions()); + private final MiscHelpersElectra miscHelpersElectra = + new MiscHelpersElectra( + spec.getGenesisSpecConfig().toVersionElectra().orElseThrow(), + predicates, + schemaDefinitionsElectra); + private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); + + @Test + public void isFormerDepositMechanismDisabled_returnsTrueIfDisabled() { + final BeaconState preState = dataStructureUtil.randomBeaconState(); + + final BeaconState state = + BeaconStateElectra.required(preState) + .updated( + mutableState -> { + final UInt64 eth1DepositIndex = dataStructureUtil.randomUInt64(); + mutableState.setEth1DepositIndex(eth1DepositIndex); + MutableBeaconStateElectra.required(mutableState) + .setDepositReceiptsStartIndex(eth1DepositIndex); + }); + + assertThat(miscHelpersElectra.isFormerDepositMechanismDisabled(state)).isTrue(); + } + + @Test + public void isFormerDepositMechanismDisabled_returnsFalseIfNotDisabled() { + final BeaconState preState = dataStructureUtil.randomBeaconState(); + + final BeaconState state = + BeaconStateElectra.required(preState) + .updated( + mutableState -> { + mutableState.setEth1DepositIndex(UInt64.valueOf(64)); + MutableBeaconStateElectra.required(mutableState) + .setDepositReceiptsStartIndex( + SpecConfigElectra.UNSET_DEPOSIT_RECEIPTS_START_INDEX); + }); + + assertThat(miscHelpersElectra.isFormerDepositMechanismDisabled(state)).isFalse(); + } +} diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecFactory.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecFactory.java index ee84d430d26..f7fa60b93ba 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecFactory.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/TestSpecFactory.java @@ -399,10 +399,11 @@ private static SpecConfigElectra getElectraSpecConfig( })); } - public static Spec createMinimalWithCapellaAndDenebForkEpoch( - final UInt64 capellaForkEpoch, final UInt64 denebForkEpoch) { + public static Spec createMinimalWithCapellaDenebAndElectraForkEpoch( + final UInt64 capellaForkEpoch, final UInt64 denebForkEpoch, final UInt64 electraForkEpoch) { final SpecConfigBellatrix config = - getDenebSpecConfig(Eth2Network.MINIMAL, capellaForkEpoch, denebForkEpoch); - return create(config, SpecMilestone.DENEB); + getElectraSpecConfig( + Eth2Network.MINIMAL, capellaForkEpoch, denebForkEpoch, electraForkEpoch); + return create(config, SpecMilestone.ELECTRA); } } diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java index 379ef746e39..987a973f774 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java @@ -52,6 +52,7 @@ import tech.pegasys.teku.infrastructure.bytes.Bytes20; import tech.pegasys.teku.infrastructure.bytes.Bytes4; import tech.pegasys.teku.infrastructure.bytes.Bytes8; +import tech.pegasys.teku.infrastructure.ssz.Merkleizable; import tech.pegasys.teku.infrastructure.ssz.SszData; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.ssz.SszPrimitive; @@ -100,10 +101,13 @@ import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockAndState; import tech.pegasys.teku.spec.datastructures.blocks.SlotAndBlockRoot; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBodyBuilder; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBodySchema; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.altair.SyncAggregate; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.altair.SyncAggregateSchema; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.capella.BeaconBlockBodySchemaCapella; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodyDeneb; +import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.deneb.BeaconBlockBodySchemaDeneb; import tech.pegasys.teku.spec.datastructures.blocks.versions.deneb.BlockContents; import tech.pegasys.teku.spec.datastructures.blocks.versions.deneb.SignedBlockContents; import tech.pegasys.teku.spec.datastructures.builder.BlobsBundleSchema; @@ -176,12 +180,8 @@ import tech.pegasys.teku.spec.datastructures.type.SszKZGProof; import tech.pegasys.teku.spec.datastructures.type.SszPublicKey; import tech.pegasys.teku.spec.datastructures.type.SszSignature; -import tech.pegasys.teku.spec.datastructures.util.DepositGenerator; import tech.pegasys.teku.spec.executionlayer.ForkChoiceState; import tech.pegasys.teku.spec.executionlayer.PayloadBuildingAttributes; -import tech.pegasys.teku.spec.logic.common.statetransition.epoch.RewardAndPenalty; -import tech.pegasys.teku.spec.logic.common.statetransition.epoch.RewardAndPenalty.RewardComponent; -import tech.pegasys.teku.spec.logic.common.statetransition.epoch.RewardAndPenaltyDeltas; import tech.pegasys.teku.spec.logic.versions.deneb.types.VersionedHash; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsAltair; @@ -213,7 +213,7 @@ public DataStructureUtil(final int seed, final Spec spec) { this.spec = spec; } - public DataStructureUtil withPubKeyGenerator(Supplier pubKeyGenerator) { + public DataStructureUtil withPubKeyGenerator(final Supplier pubKeyGenerator) { this.pubKeyGenerator = pubKeyGenerator; return this; } @@ -324,27 +324,30 @@ public SszList randomSszList( public > SszPrimitiveList randomSszPrimitiveList( - SszPrimitiveListSchema schema, + final SszPrimitiveListSchema schema, final long numItems, - Supplier valueGenerator) { + final Supplier valueGenerator) { return Stream.generate(valueGenerator) .limit(Math.min(numItems, schema.getMaxLength())) .collect(schema.collectorUnboxed()); } - public SszUInt64List randomSszUInt64List(SszUInt64ListSchema schema, final long numItems) { + public SszUInt64List randomSszUInt64List( + final SszUInt64ListSchema schema, final long numItems) { return randomSszUInt64List(schema, numItems, this::randomUInt64); } public SszUInt64List randomSszUInt64List( - SszUInt64ListSchema schema, final long numItems, Supplier valueGenerator) { + final SszUInt64ListSchema schema, + final long numItems, + final Supplier valueGenerator) { return Stream.generate(valueGenerator).limit(numItems).collect(schema.collectorUnboxed()); } public SszBytes32Vector randomSszBytes32Vector( - SszBytes32VectorSchema schema, Supplier valueGenerator) { - int numItems = schema.getLength() / 10; - Bytes32 defaultElement = schema.getPrimitiveElementSchema().getDefault().get(); + final SszBytes32VectorSchema schema, final Supplier valueGenerator) { + final int numItems = schema.getLength() / 10; + final Bytes32 defaultElement = schema.getPrimitiveElementSchema().getDefault().get(); return Stream.concat( Stream.generate(valueGenerator).limit(numItems), Stream.generate(() -> defaultElement).limit(schema.getLength() - numItems)) @@ -353,10 +356,10 @@ public SszBytes32Vector randomSszBytes32Vector( public > SszPrimitiveVector randomSszPrimitiveVector( - SszPrimitiveVectorSchema schema, - Supplier valueGenerator) { - int numItems = schema.getLength() / 10; - ElementT defaultElement = schema.getPrimitiveElementSchema().getDefault().get(); + final SszPrimitiveVectorSchema schema, + final Supplier valueGenerator) { + final int numItems = schema.getLength() / 10; + final ElementT defaultElement = schema.getPrimitiveElementSchema().getDefault().get(); return Stream.concat( Stream.generate(valueGenerator).limit(numItems), Stream.generate(() -> defaultElement).limit(schema.getLength() - numItems)) @@ -366,8 +369,8 @@ SszPrimitiveVector randomSszPrimitiveVector( public > VectorT randomSszVector( SszVectorSchema schema, Supplier valueGenerator) { - int numItems = schema.getLength() / 10; - SszElementT defaultElement = schema.getElementSchema().getDefault(); + final int numItems = schema.getLength() / 10; + final SszElementT defaultElement = schema.getElementSchema().getDefault(); return Stream.concat( Stream.generate(valueGenerator).limit(numItems), Stream.generate(() -> defaultElement).limit(schema.getLength() - numItems)) @@ -378,21 +381,17 @@ public SszByte randomSszByte() { return SszByte.of(randomByte()); } - public SszBytes32 randomSszBytes32() { - return SszBytes32.of(randomBytes32()); - } - public SszBitlist randomBitlist() { return randomBitlist(getMaxValidatorsPerCommittee()); } - public SszBitlist randomBitlist(int n) { + public SszBitlist randomBitlist(final int n) { Random random = new Random(nextSeed()); int[] bits = IntStream.range(0, n).sequential().filter(__ -> random.nextBoolean()).toArray(); return SszBitlistSchema.create(n).ofBits(n, bits); } - public SszBitvector randomSszBitvector(int n) { + public SszBitvector randomSszBitvector(final int n) { Random random = new Random(nextSeed()); int[] bits = IntStream.range(0, n).sequential().filter(__ -> random.nextBoolean()).toArray(); return SszBitvectorSchema.create(n).ofBits(bits); @@ -483,15 +482,12 @@ public Checkpoint randomCheckpoint() { return new Checkpoint(randomEpoch(), randomBytes32()); } - public SyncAggregate randomSyncAggregateIfRequiredBySchema(BeaconBlockBodySchema schema) { + public SyncAggregate randomSyncAggregateIfRequiredBySchema( + final BeaconBlockBodySchema schema) { return schema.toVersionAltair().map(__ -> randomSyncAggregate()).orElse(null); } - public SyncAggregate randomSyncAggregateIfRequiredByState(BeaconState state) { - return state.toVersionAltair().map(__ -> randomSyncAggregate()).orElse(null); - } - - public SyncAggregate emptySyncAggregateIfRequiredByState(BeaconState state) { + public SyncAggregate emptySyncAggregateIfRequiredByState(final BeaconState state) { return state.toVersionAltair().map(__ -> emptySyncAggregate()).orElse(null); } @@ -500,21 +496,21 @@ public SyncAggregate randomSyncAggregate() { } public SyncAggregate emptySyncAggregate() { - SpecVersion specVersionAltair = + final SpecVersion specVersionAltair = Optional.ofNullable(spec.forMilestone(SpecMilestone.ALTAIR)).orElseThrow(); return getSyncAggregateSchema(specVersionAltair).createEmpty(); } public SyncAggregate randomSyncAggregate(final int... participantIndices) { - SpecVersion specVersionAltair = + final SpecVersion specVersionAltair = Optional.ofNullable(spec.forMilestone(SpecMilestone.ALTAIR)).orElseThrow(); return getSyncAggregateSchema(specVersionAltair) .create(IntList.of(participantIndices), randomSignature()); } - private SyncAggregateSchema getSyncAggregateSchema(SpecVersion specVersionAltair) { + private SyncAggregateSchema getSyncAggregateSchema(final SpecVersion specVersionAltair) { return SchemaDefinitionsAltair.required(specVersionAltair.getSchemaDefinitions()) .getBeaconBlockBodySchema() .toVersionAltair() @@ -523,7 +519,7 @@ private SyncAggregateSchema getSyncAggregateSchema(SpecVersion specVersionAltair } public SyncAggregatorSelectionData randomSyncAggregatorSelectionData() { - SpecVersion specVersionAltair = + final SpecVersion specVersionAltair = Optional.ofNullable(spec.forMilestone(SpecMilestone.ALTAIR)).orElseThrow(); return specVersionAltair .getSchemaDefinitions() @@ -533,6 +529,12 @@ public SyncAggregatorSelectionData randomSyncAggregatorSelectionData() { .create(randomUInt64(), randomUInt64()); } + public BlobKzgCommitmentsSchema getBlobKzgCommitmentsSchema() { + return SchemaDefinitionsDeneb.required( + spec.forMilestone(SpecMilestone.DENEB).getSchemaDefinitions()) + .getBlobKzgCommitmentsSchema(); + } + public SyncCommittee randomSyncCommittee() { final SyncCommitteeSchema syncCommitteeSchema = ((BeaconStateSchemaAltair) @@ -546,7 +548,7 @@ public SyncCommittee randomSyncCommittee() { new SszPublicKey(randomPublicKey())); } - public SyncCommittee randomSyncCommittee(SszList validators) { + public SyncCommittee randomSyncCommittee(final SszList validators) { final SyncCommitteeSchema syncCommitteeSchema = ((BeaconStateSchemaAltair) spec.forMilestone(SpecMilestone.ALTAIR) @@ -668,11 +670,11 @@ public ExecutionPayload randomExecutionPayload() { } public ExecutionPayload randomExecutionPayload(final UInt64 slot) { - return randomExecutionPayload(slot, executionPayloadBuilder -> {}); + return randomExecutionPayload(slot, __ -> {}); } public ExecutionPayload randomExecutionPayload( - final UInt64 slot, final Consumer postRandomModifications) { + final UInt64 slot, final Consumer builderModifier) { final SpecConfigBellatrix specConfigBellatrix = SpecConfigBellatrix.required(spec.atSlot(slot).getConfig()); return getBellatrixSchemaDefinitions(slot) @@ -700,7 +702,7 @@ public ExecutionPayload randomExecutionPayload( .excessBlobGas(this::randomUInt64) .depositReceipts(this::randomExecutionPayloadDepositReceipts) .exits(this::randomExecutionPayloadExits); - postRandomModifications.accept(executionPayloadBuilder); + builderModifier.accept(executionPayloadBuilder); }); } @@ -922,12 +924,12 @@ public SignedBeaconBlock randomSignedBlindedBeaconBlock() { return signedBlock(beaconBlock); } - public SignedBeaconBlock randomSignedBlindedBeaconBlock(long slotNum) { + public SignedBeaconBlock randomSignedBlindedBeaconBlock(final long slotNum) { final BeaconBlock beaconBlock = randomBlindedBeaconBlock(UInt64.valueOf(slotNum)); return signedBlock(beaconBlock); } - public SignedBeaconBlock randomSignedBlindedBeaconBlock(UInt64 slotNum) { + public SignedBeaconBlock randomSignedBlindedBeaconBlock(final UInt64 slotNum) { final BeaconBlock beaconBlock = randomBlindedBeaconBlock(slotNum); return signedBlock(beaconBlock); } @@ -957,20 +959,21 @@ public SignedBeaconBlock randomSignedBeaconBlock() { return randomSignedBeaconBlock(randomUInt64()); } - public SignedBeaconBlock randomSignedBeaconBlock(long slotNum) { + public SignedBeaconBlock randomSignedBeaconBlock(final long slotNum) { return randomSignedBeaconBlock(UInt64.valueOf(slotNum)); } - public SignedBeaconBlock randomSignedBeaconBlock(UInt64 slotNum) { + public SignedBeaconBlock randomSignedBeaconBlock(final UInt64 slotNum) { final BeaconBlock beaconBlock = randomBeaconBlock(slotNum); return signedBlock(beaconBlock); } - public SignedBeaconBlock randomSignedBeaconBlock(long slotNum, Bytes32 parentRoot) { + public SignedBeaconBlock randomSignedBeaconBlock(final long slotNum, final Bytes32 parentRoot) { return randomSignedBeaconBlock(slotNum, parentRoot, false); } - public SignedBeaconBlock randomSignedBeaconBlock(long slotNum, Bytes32 parentRoot, boolean full) { + public SignedBeaconBlock randomSignedBeaconBlock( + final long slotNum, final Bytes32 parentRoot, final boolean full) { final BeaconBlock beaconBlock = randomBeaconBlock(slotNum, parentRoot, full); return signedBlock(beaconBlock); } @@ -1026,11 +1029,11 @@ public SignedBeaconBlock signedBlock(final BeaconBlock block, final BLSSignature return SignedBeaconBlock.create(spec, block, signature); } - public SignedBeaconBlock randomSignedBeaconBlock(long slotNum, BeaconState state) { + public SignedBeaconBlock randomSignedBeaconBlock(final long slotNum, final BeaconState state) { return randomSignedBeaconBlock(UInt64.valueOf(slotNum), state); } - public SignedBeaconBlock randomSignedBeaconBlock(UInt64 slotNum, BeaconState state) { + public SignedBeaconBlock randomSignedBeaconBlock(final UInt64 slotNum, final BeaconState state) { final BeaconBlockBody body = randomBeaconBlockBody(); final Bytes32 stateRoot = state.hashTreeRoot(); @@ -1050,15 +1053,15 @@ public BeaconBlock randomBeaconBlock() { return randomBeaconBlock(randomUInt64()); } - public BeaconBlock randomBeaconBlock(long slotNum) { + public BeaconBlock randomBeaconBlock(final long slotNum) { return randomBeaconBlock(UInt64.valueOf(slotNum)); } - public BeaconBlock randomBeaconBlock(UInt64 slotNum) { + public BeaconBlock randomBeaconBlock(final UInt64 slotNum) { final UInt64 proposerIndex = randomUInt64(); - Bytes32 previousRoot = randomBytes32(); - Bytes32 stateRoot = randomBytes32(); - BeaconBlockBody body = randomBeaconBlockBody(slotNum); + final Bytes32 previousRoot = randomBytes32(); + final Bytes32 stateRoot = randomBytes32(); + final BeaconBlockBody body = randomBeaconBlockBody(slotNum); return new BeaconBlock( spec.atSlot(slotNum).getSchemaDefinitions().getBeaconBlockSchema(), @@ -1073,7 +1076,7 @@ public BeaconBlock randomBlindedBeaconBlock() { return randomBlindedBeaconBlock(randomUInt64()); } - public BeaconBlock randomBlindedBeaconBlock(long slotNum) { + public BeaconBlock randomBlindedBeaconBlock(final long slotNum) { return randomBlindedBeaconBlock(UInt64.valueOf(slotNum)); } @@ -1089,10 +1092,6 @@ public BlockContainerAndMetaData randomBlockContainerAndMetaData(final UInt64 sl randomUInt256()); } - public BlockContainerAndMetaData randomBlindedBlockContainerAndMetaData(final long slotNum) { - return randomBlindedBlockContainerAndMetaData(UInt64.valueOf(slotNum)); - } - public BlockContainerAndMetaData randomBlindedBlockContainerAndMetaData(final UInt64 slotNum) { return new BlockContainerAndMetaData( randomBlindedBeaconBlock(slotNum), @@ -1107,15 +1106,15 @@ public BlockContainerAndMetaData randomBlockContainerAndMetaData( blockContents, spec.atSlot(slotNum).getMilestone(), randomUInt256(), randomUInt256()); } - public BeaconBlock randomBlindedBeaconBlock(UInt64 slotNum) { + public BeaconBlock randomBlindedBeaconBlock(final UInt64 slot) { final UInt64 proposerIndex = randomUInt64(); - Bytes32 previousRoot = randomBytes32(); - Bytes32 stateRoot = randomBytes32(); - BeaconBlockBody body = randomBlindedBeaconBlockBody(slotNum); + final Bytes32 previousRoot = randomBytes32(); + final Bytes32 stateRoot = randomBytes32(); + final BeaconBlockBody body = randomBlindedBeaconBlockBody(slot); return new BeaconBlock( - spec.atSlot(slotNum).getSchemaDefinitions().getBlindedBeaconBlockSchema(), - slotNum, + spec.atSlot(slot).getSchemaDefinitions().getBlindedBeaconBlockSchema(), + slot, proposerIndex, previousRoot, stateRoot, @@ -1149,7 +1148,7 @@ public SignedBlockAndState randomSignedBlockAndStateWithValidatorLogic(final int return toSigned(blockAndState); } - public SignedBlockAndState toSigned(BeaconBlockAndState blockAndState) { + public SignedBlockAndState toSigned(final BeaconBlockAndState blockAndState) { final SignedBeaconBlock signedBlock = signedBlock(blockAndState.getBlock()); return new SignedBlockAndState(signedBlock, blockAndState.getState()); } @@ -1212,19 +1211,20 @@ private BeaconBlockAndState randomBlockAndStateWithValidatorLogic( return new BeaconBlockAndState(block, matchingState); } - public BeaconBlock randomBeaconBlock(long slotNum, Bytes32 parentRoot, boolean isFull) { + public BeaconBlock randomBeaconBlock( + final long slotNum, final Bytes32 parentRoot, final boolean isFull) { return randomBeaconBlock(slotNum, parentRoot, randomBytes32(), isFull); } public BeaconBlock randomBeaconBlock( - long slot, Bytes32 parentRoot, final Bytes32 stateRoot, boolean isFull) { + final long slot, Bytes32 parentRoot, final Bytes32 stateRoot, final boolean isFull) { return randomBeaconBlock(UInt64.valueOf(slot), parentRoot, stateRoot, isFull); } public BeaconBlock randomBeaconBlock( - UInt64 slot, Bytes32 parentRoot, final Bytes32 stateRoot, boolean isFull) { + final UInt64 slot, final Bytes32 parentRoot, final Bytes32 stateRoot, final boolean isFull) { final UInt64 proposerIndex = randomUInt64(); - BeaconBlockBody body = !isFull ? randomBeaconBlockBody() : randomFullBeaconBlockBody(); + final BeaconBlockBody body = !isFull ? randomBeaconBlockBody() : randomFullBeaconBlockBody(); return new BeaconBlock( spec.atSlot(slot).getSchemaDefinitions().getBeaconBlockSchema(), @@ -1235,7 +1235,7 @@ public BeaconBlock randomBeaconBlock( body); } - public BeaconBlock randomBeaconBlock(long slotNum, Bytes32 parentRoot) { + public BeaconBlock randomBeaconBlock(final long slotNum, final Bytes32 parentRoot) { return randomBeaconBlock(slotNum, parentRoot, false); } @@ -1263,97 +1263,28 @@ public BeaconBlockHeader randomBeaconBlockHeader(final UInt64 slot, final UInt64 } public BeaconBlockBody randomBlindedBeaconBlockBody() { - return randomBlindedBeaconBlockBody(randomUInt64()); + return randomBlindedBeaconBlockBody(randomUInt64(), __ -> {}); } public BeaconBlockBody randomBlindedBeaconBlockBodyWithCommitments( final UInt64 slot, final SszList commitments) { - final BeaconBlockBodySchema schema = - spec.atSlot(slot).getSchemaDefinitions().getBlindedBeaconBlockBodySchema(); - - return schema - .createBlockBody( - builder -> { - builder - .randaoReveal(randomSignature()) - .eth1Data(randomEth1Data()) - .graffiti(Bytes32.ZERO) - .proposerSlashings( - randomSszList( - schema.getProposerSlashingsSchema(), this::randomProposerSlashing, 1)) - .attesterSlashings( - randomSszList( - schema.getAttesterSlashingsSchema(), this::randomAttesterSlashing, 1)) - .attestations( - randomSszList(schema.getAttestationsSchema(), this::randomAttestation, 3)) - .deposits( - randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) - .voluntaryExits( - randomSszList( - schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)) - .syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); - if (builder.supportsSyncAggregate()) { - builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); - } - if (builder.supportsExecutionPayload()) { - builder.executionPayloadHeader(randomExecutionPayloadHeader(spec.atSlot(slot))); - } - if (builder.supportsBlsToExecutionChanges()) { - builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); - } - if (builder.supportsKzgCommitments()) { - builder.blobKzgCommitments(commitments); - } - return SafeFuture.COMPLETE; - }) - .join(); + return randomBlindedBeaconBlockBody( + slot, + builder -> { + if (builder.supportsKzgCommitments()) { + builder.blobKzgCommitments(commitments); + } + }); } - public BeaconBlockBody randomBlindedBeaconBlockBody(UInt64 slotNum) { - BeaconBlockBodySchema schema = - spec.atSlot(slotNum).getSchemaDefinitions().getBlindedBeaconBlockBodySchema(); - - return schema - .createBlockBody( - builder -> { - builder - .randaoReveal(randomSignature()) - .eth1Data(randomEth1Data()) - .graffiti(Bytes32.ZERO) - .proposerSlashings( - randomSszList( - schema.getProposerSlashingsSchema(), this::randomProposerSlashing, 1)) - .attesterSlashings( - randomSszList( - schema.getAttesterSlashingsSchema(), this::randomAttesterSlashing, 1)) - .attestations( - randomSszList(schema.getAttestationsSchema(), this::randomAttestation, 3)) - .deposits( - randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) - .voluntaryExits( - randomSszList( - schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); - if (builder.supportsSyncAggregate()) { - builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); - } - if (builder.supportsExecutionPayload()) { - builder.executionPayloadHeader(randomExecutionPayloadHeader(spec.atSlot(slotNum))); - } - if (builder.supportsBlsToExecutionChanges()) { - builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); - } - if (builder.supportsKzgCommitments()) { - builder.blobKzgCommitments(randomBlobKzgCommitments()); - } - return SafeFuture.COMPLETE; - }) - .join(); + public BeaconBlockBody randomBlindedBeaconBlockBody(final UInt64 slot) { + return randomBlindedBeaconBlockBody(slot, __ -> {}); } - public BeaconBlockBody randomBeaconBlockBody(final UInt64 slotNum) { - BeaconBlockBodySchema schema = - spec.atSlot(slotNum).getSchemaDefinitions().getBeaconBlockBodySchema(); - + public BeaconBlockBody randomBlindedBeaconBlockBody( + final UInt64 slot, final Consumer builderModifier) { + final BeaconBlockBodySchema schema = + spec.atSlot(slot).getSchemaDefinitions().getBlindedBeaconBlockBodySchema(); return schema .createBlockBody( builder -> { @@ -1378,7 +1309,7 @@ public BeaconBlockBody randomBeaconBlockBody(final UInt64 slotNum) { builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); } if (builder.supportsExecutionPayload()) { - builder.executionPayload(randomExecutionPayload(slotNum)); + builder.executionPayloadHeader(randomExecutionPayloadHeader(spec.atSlot(slot))); } if (builder.supportsBlsToExecutionChanges()) { builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); @@ -1386,131 +1317,51 @@ public BeaconBlockBody randomBeaconBlockBody(final UInt64 slotNum) { if (builder.supportsKzgCommitments()) { builder.blobKzgCommitments(randomBlobKzgCommitments()); } + builderModifier.accept(builder); return SafeFuture.COMPLETE; }) .join(); } public BeaconBlockBody randomBeaconBlockBody() { - BeaconBlockBodySchema schema = - spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); - return schema - .createBlockBody( - builder -> { - builder - .randaoReveal(randomSignature()) - .eth1Data(randomEth1Data()) - .graffiti(Bytes32.ZERO) - .proposerSlashings( - randomSszList( - schema.getProposerSlashingsSchema(), this::randomProposerSlashing, 1)) - .attesterSlashings( - randomSszList( - schema.getAttesterSlashingsSchema(), this::randomAttesterSlashing, 1)) - .attestations( - randomSszList(schema.getAttestationsSchema(), this::randomAttestation, 3)) - .deposits( - randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) - .voluntaryExits( - randomSszList( - schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); - if (builder.supportsSyncAggregate()) { - builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); - } - if (builder.supportsExecutionPayload()) { - builder.executionPayload(randomExecutionPayload()); - } - if (builder.supportsBlsToExecutionChanges()) { - builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); - } - if (builder.supportsKzgCommitments()) { - builder.blobKzgCommitments(randomBlobKzgCommitments()); - } - return SafeFuture.COMPLETE; - }) - .join(); + return randomBeaconBlockBody(randomUInt64(), __ -> {}); + } + + public BeaconBlockBody randomBeaconBlockBody(final UInt64 slot) { + return randomBeaconBlockBody(slot, __ -> {}); } public BeaconBlockBody randomBeaconBlockBody( final UInt64 proposalSlot, final int validatorCount) { Preconditions.checkArgument( proposalSlot.isGreaterThan(1), "Proposal slot must be greater than 1"); - BeaconBlockBodySchema schema = - spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); - return schema - .createBlockBody( - builder -> { - builder - .randaoReveal(randomSignature()) - .eth1Data(randomEth1Data()) - .graffiti(Bytes32.ZERO) - .proposerSlashings( - randomSszList( - schema.getProposerSlashingsSchema(), - () -> - randomProposerSlashing( - randomUInt64(proposalSlot.decrement().longValue()), - randomUInt64(validatorCount - 1)), - 1)) - .attesterSlashings( - randomSszList( - schema.getAttesterSlashingsSchema(), - () -> randomAttesterSlashing(randomUInt64(validatorCount - 1)), - 1)) - .attestations( - randomSszList(schema.getAttestationsSchema(), this::randomAttestation, 3)) - .deposits( - randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) - .voluntaryExits( - randomSszList( - schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); - if (builder.supportsSyncAggregate()) { - builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); - } - if (builder.supportsExecutionPayload()) { - builder.executionPayload(randomExecutionPayload(proposalSlot)); - } - if (builder.supportsBlsToExecutionChanges()) { - builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); - } - if (builder.supportsKzgCommitments()) { - builder.blobKzgCommitments(randomBlobKzgCommitments()); - } - return SafeFuture.COMPLETE; - }) - .join(); + final BeaconBlockBodySchema schema = + spec.atSlot(proposalSlot).getSchemaDefinitions().getBeaconBlockBodySchema(); + return randomBeaconBlockBody( + proposalSlot, + builder -> { + builder + .proposerSlashings( + randomSszList( + schema.getProposerSlashingsSchema(), + () -> + randomProposerSlashing( + randomUInt64(proposalSlot.decrement().longValue()), + randomUInt64(validatorCount - 1)), + 1)) + .attesterSlashings( + randomSszList( + schema.getAttesterSlashingsSchema(), + () -> randomAttesterSlashing(randomUInt64(validatorCount - 1)), + 1)); + if (builder.supportsExecutionPayload()) { + builder.executionPayload(randomExecutionPayload(proposalSlot)); + } + }); } public BeaconBlockBody randomBeaconBlockBodyWithEmptyCommitments() { - BeaconBlockBodySchema schema = - spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); - return schema - .createBlockBody( - builder -> { - builder - .randaoReveal(randomSignature()) - .eth1Data(randomEth1Data()) - .graffiti(Bytes32.ZERO) - .proposerSlashings( - randomSszList( - schema.getProposerSlashingsSchema(), this::randomProposerSlashing, 1)) - .attesterSlashings( - randomSszList( - schema.getAttesterSlashingsSchema(), this::randomAttesterSlashing, 1)) - .attestations( - randomSszList(schema.getAttestationsSchema(), this::randomAttestation, 3)) - .deposits( - randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) - .voluntaryExits( - randomSszList( - schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)) - .syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)) - .executionPayload(randomExecutionPayload()) - .blsToExecutionChanges(randomSignedBlsToExecutionChangesList()) - .blobKzgCommitments(emptyBlobKzgCommitments()); - return SafeFuture.COMPLETE; - }) - .join(); + return randomBeaconBlockBody(builder -> builder.blobKzgCommitments(emptyBlobKzgCommitments())); } public BeaconBlockBody randomBeaconBlockBodyWithCommitments(final int count) { @@ -1519,8 +1370,23 @@ public BeaconBlockBody randomBeaconBlockBodyWithCommitments(final int count) { public BeaconBlockBody randomBeaconBlockBodyWithCommitments( final SszList commitments) { + return randomBeaconBlockBody( + builder -> { + if (builder.supportsKzgCommitments()) { + builder.blobKzgCommitments(commitments); + } + }); + } + + public BeaconBlockBody randomBeaconBlockBody( + final Consumer builderModifier) { + return randomBeaconBlockBody(randomUInt64(), builderModifier); + } + + public BeaconBlockBody randomBeaconBlockBody( + final UInt64 slot, final Consumer builderModifier) { final BeaconBlockBodySchema schema = - spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); + spec.atSlot(slot).getSchemaDefinitions().getBeaconBlockBodySchema(); return schema .createBlockBody( builder -> { @@ -1540,17 +1406,31 @@ public BeaconBlockBody randomBeaconBlockBodyWithCommitments( randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, 1)) .voluntaryExits( randomSszList( - schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)) - .syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)) - .executionPayload(randomExecutionPayload()) - .blsToExecutionChanges(randomSignedBlsToExecutionChangesList()) - .blobKzgCommitments(commitments); + schema.getVoluntaryExitsSchema(), this::randomSignedVoluntaryExit, 1)); + if (builder.supportsSyncAggregate()) { + builder.syncAggregate(randomSyncAggregateIfRequiredBySchema(schema)); + } + if (builder.supportsExecutionPayload()) { + builder.executionPayload(randomExecutionPayload(slot)); + } + if (builder.supportsBlsToExecutionChanges()) { + builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); + } + if (builder.supportsKzgCommitments()) { + builder.blobKzgCommitments(randomBlobKzgCommitments()); + } + builderModifier.accept(builder); return SafeFuture.COMPLETE; }) .join(); } public BeaconBlockBody randomFullBeaconBlockBody() { + return randomFullBeaconBlockBody(__ -> {}); + } + + public BeaconBlockBody randomFullBeaconBlockBody( + final Consumer builderModifier) { final BeaconBlockBodySchema schema = spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); return schema @@ -1581,11 +1461,19 @@ public BeaconBlockBody randomFullBeaconBlockBody() { builder.executionPayload(randomExecutionPayload()); } if (builder.supportsBlsToExecutionChanges()) { - builder.blsToExecutionChanges(randomSignedBlsToExecutionChangesList()); + builder.blsToExecutionChanges( + randomFullSszList( + BeaconBlockBodySchemaCapella.required(schema) + .getBlsToExecutionChangesSchema(), + this::randomSignedBlsToExecutionChange)); } if (builder.supportsKzgCommitments()) { - builder.blobKzgCommitments(randomBlobKzgCommitments()); + builder.blobKzgCommitments( + randomFullSszList( + BeaconBlockBodySchemaDeneb.required(schema).getBlobKzgCommitmentsSchema(), + this::randomSszKZGCommitment)); } + builderModifier.accept(builder); return SafeFuture.COMPLETE; }) .join(); @@ -1595,11 +1483,11 @@ public ProposerSlashing randomProposerSlashing() { return randomProposerSlashing(randomUInt64(), randomUInt64()); } - public ProposerSlashing randomProposerSlashing(int validatorLimit) { + public ProposerSlashing randomProposerSlashing(final int validatorLimit) { return randomProposerSlashing(randomUInt64(), randomUInt64(validatorLimit)); } - public AttesterSlashing randomAttesterSlashing(int validatorLimit) { + public AttesterSlashing randomAttesterSlashing(final int validatorLimit) { return randomAttesterSlashing(randomUInt64(validatorLimit)); } @@ -1621,41 +1509,41 @@ public IndexedAttestation randomIndexedAttestation( final AttestationData data, final UInt64... attestingIndicesInput) { final IndexedAttestationSchema indexedAttestationSchema = spec.getGenesisSchemaDefinitions().getIndexedAttestationSchema(); - SszUInt64List attestingIndices = + final SszUInt64List attestingIndices = indexedAttestationSchema.getAttestingIndicesSchema().of(attestingIndicesInput); return indexedAttestationSchema.create(attestingIndices, data, randomSignature()); } - public DepositMessage randomDepositMessage(BLSKeyPair keyPair) { - BLSPublicKey pubkey = keyPair.getPublicKey(); - Bytes32 withdrawalCredentials = randomBytes32(); + public DepositMessage randomDepositMessage(final BLSKeyPair keyPair) { + final BLSPublicKey pubkey = keyPair.getPublicKey(); + final Bytes32 withdrawalCredentials = randomBytes32(); return new DepositMessage(pubkey, withdrawalCredentials, getMaxEffectiveBalance()); } public DepositMessage randomDepositMessage() { - BLSKeyPair keyPair = BLSTestUtil.randomKeyPair(nextSeed()); + final BLSKeyPair keyPair = randomKeyPair(); return randomDepositMessage(keyPair); } public DepositData randomDepositData() { - BLSKeyPair keyPair = BLSTestUtil.randomKeyPair(nextSeed()); - DepositMessage proofOfPossessionData = randomDepositMessage(keyPair); + final BLSKeyPair keyPair = randomKeyPair(); + final DepositMessage depositMessage = randomDepositMessage(keyPair); - final Bytes32 domain = computeDomain(); - final Bytes signingRoot = getSigningRoot(proofOfPossessionData, domain); + final Bytes32 domain = computeDepositDomain(); + final Bytes signingRoot = getSigningRoot(depositMessage, domain); - BLSSignature proofOfPossession = BLS.sign(keyPair.getSecretKey(), signingRoot); + final BLSSignature signature = BLS.sign(keyPair.getSecretKey(), signingRoot); - return new DepositData(proofOfPossessionData, proofOfPossession); + return new DepositData(depositMessage, signature); } public DepositWithIndex randomDepositWithIndex() { return randomDepositWithIndex(randomLong()); } - public DepositWithIndex randomDepositWithIndex(long depositIndex) { - Bytes32 randomBytes32 = randomBytes32(); - SszBytes32VectorSchema proofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); + public DepositWithIndex randomDepositWithIndex(final long depositIndex) { + final Bytes32 randomBytes32 = randomBytes32(); + final SszBytes32VectorSchema proofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); return new DepositWithIndex( Stream.generate(() -> randomBytes32) .limit(proofSchema.getLength()) @@ -1665,14 +1553,18 @@ public DepositWithIndex randomDepositWithIndex(long depositIndex) { } public DepositsFromBlockEvent randomDepositsFromBlockEvent( - final long blockIndex, long depositIndexStartInclusive, long depositIndexEndExclusive) { + final long blockIndex, + final long depositIndexStartInclusive, + final long depositIndexEndExclusive) { return randomDepositsFromBlockEvent( UInt64.valueOf(blockIndex), depositIndexStartInclusive, depositIndexEndExclusive); } public DepositsFromBlockEvent randomDepositsFromBlockEvent( - UInt64 blockIndex, long depositIndexStartInclusive, long depositIndexEndExclusive) { - List deposits = new ArrayList<>(); + final UInt64 blockIndex, + final long depositIndexStartInclusive, + final long depositIndexEndExclusive) { + final List deposits = new ArrayList<>(); for (long i = depositIndexStartInclusive; i < depositIndexEndExclusive; i++) { deposits.add(randomDepositEvent(UInt64.valueOf(i))); } @@ -1686,8 +1578,8 @@ public MinGenesisTimeBlockEvent randomMinGenesisTimeBlockEvent(final long blockI } public Deposit randomDepositWithoutIndex() { - Bytes32 randomBytes32 = randomBytes32(); - SszBytes32VectorSchema proofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); + final Bytes32 randomBytes32 = randomBytes32(); + final SszBytes32VectorSchema proofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); return new Deposit( Stream.generate(() -> randomBytes32) .limit(proofSchema.getLength()) @@ -1696,8 +1588,8 @@ public Deposit randomDepositWithoutIndex() { } public Deposit randomDeposit() { - Bytes32 randomBytes32 = randomBytes32(); - SszBytes32VectorSchema proofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); + final Bytes32 randomBytes32 = randomBytes32(); + final SszBytes32VectorSchema proofSchema = Deposit.SSZ_SCHEMA.getProofSchema(); return new Deposit( Stream.generate(() -> randomBytes32) .limit(proofSchema.getLength()) @@ -1705,11 +1597,11 @@ public Deposit randomDeposit() { randomDepositData()); } - public tech.pegasys.teku.ethereum.pow.api.Deposit randomDepositEvent(long index) { + public tech.pegasys.teku.ethereum.pow.api.Deposit randomDepositEvent(final long index) { return randomDepositEvent(UInt64.valueOf(index)); } - public tech.pegasys.teku.ethereum.pow.api.Deposit randomDepositEvent(UInt64 index) { + public tech.pegasys.teku.ethereum.pow.api.Deposit randomDepositEvent(final UInt64 index) { return new tech.pegasys.teku.ethereum.pow.api.Deposit( BLSTestUtil.randomPublicKey(nextSeed()), randomBytes32(), @@ -1722,8 +1614,8 @@ public tech.pegasys.teku.ethereum.pow.api.Deposit randomDepositEvent() { return randomDepositEvent(randomUInt64()); } - public List randomDeposits(int num) { - ArrayList deposits = new ArrayList<>(); + public List randomDeposits(final int num) { + final ArrayList deposits = new ArrayList<>(); for (int i = 0; i < num; i++) { deposits.add(randomDepositWithIndex()); @@ -1732,6 +1624,12 @@ public List randomDeposits(int num) { return deposits; } + public SszList randomSszDeposits(final int num) { + final BeaconBlockBodySchema schema = + spec.getGenesisSpec().getSchemaDefinitions().getBeaconBlockBodySchema(); + return randomSszList(schema.getDepositsSchema(), this::randomDepositWithoutIndex, num); + } + public DepositTreeSnapshot randomDepositTreeSnapshot() { return randomDepositTreeSnapshot(randomLong(), randomUInt64()); } @@ -1762,24 +1660,6 @@ public VoluntaryExit randomVoluntaryExit(final UInt64 validatorIndex) { return new VoluntaryExit(randomUInt64(), validatorIndex); } - public List newDeposits(int numDeposits) { - List deposits = new ArrayList<>(); - final DepositGenerator depositGenerator = new DepositGenerator(spec); - - for (int i = 0; i < numDeposits; i++) { - BLSKeyPair keypair = BLSTestUtil.randomKeyPair(i); - DepositData depositData = - depositGenerator.createDepositData( - keypair, getMaxEffectiveBalance(), keypair.getPublicKey()); - - DepositWithIndex deposit = - new DepositWithIndex( - Deposit.SSZ_SCHEMA.getProofSchema().getDefault(), depositData, UInt64.valueOf(i)); - deposits.add(deposit); - } - return deposits; - } - public Validator randomValidator() { return randomValidator(randomPublicKey()); } @@ -1985,10 +1865,6 @@ public BeaconStateBuilderAltair stateBuilderAltair( this, spec, defaultValidatorCount, defaultItemsInSSZLists); } - public BeaconStateBuilderBellatrix stateBuilderBellatrix() { - return stateBuilderBellatrix(10, 10); - } - public BeaconStateBuilderBellatrix stateBuilderBellatrix( final int defaultValidatorCount, final int defaultItemsInSSZLists) { return BeaconStateBuilderBellatrix.create( @@ -2005,31 +1881,23 @@ public BeaconStateBuilderCapella stateBuilderCapella( this, spec, defaultValidatorCount, defaultItemsInSSZLists); } - public BeaconStateBuilderDeneb stateBuilderDeneb() { - return stateBuilderDeneb(10, 10); - } - public BeaconStateBuilderDeneb stateBuilderDeneb( final int defaultValidatorCount, final int defaultItemsInSSZLists) { return BeaconStateBuilderDeneb.create( this, spec, defaultValidatorCount, defaultItemsInSSZLists); } - public BeaconStateBuilderElectra stateBuilderElectra() { - return stateBuilderElectra(10, 10); - } - public BeaconStateBuilderElectra stateBuilderElectra( final int defaultValidatorCount, final int defaultItemsInSSZLists) { return BeaconStateBuilderElectra.create( this, spec, defaultValidatorCount, defaultItemsInSSZLists); } - public BeaconState randomBeaconState(UInt64 slot) { + public BeaconState randomBeaconState(final UInt64 slot) { return randomBeaconState().updated(state -> state.setSlot(slot)); } - public BeaconState randomBeaconStatePreMerge(UInt64 slot) { + public BeaconState randomBeaconStatePreMerge(final UInt64 slot) { return randomBeaconState() .updated(state -> state.setSlot(slot)) .updated( @@ -2043,10 +1911,6 @@ public BeaconState randomBeaconStatePreMerge(UInt64 slot) { .getDefault())); } - public AnchorPoint randomAnchorPoint(final long epoch) { - return randomAnchorPoint(UInt64.valueOf(epoch)); - } - public AnchorPoint randomAnchorPoint(final UInt64 epoch) { final SignedBlockAndState anchorBlockAndState = randomSignedBlockAndState(computeStartSlotAtEpoch(epoch)); @@ -2054,7 +1918,7 @@ public AnchorPoint randomAnchorPoint(final UInt64 epoch) { } public AnchorPoint randomAnchorPoint(final UInt64 epoch, final Fork currentFork) { - UInt64 slot = computeStartSlotAtEpoch(epoch); + final UInt64 slot = computeStartSlotAtEpoch(epoch); final BeaconBlockAndState blockAndState = randomBlockAndState( slot, @@ -2147,9 +2011,9 @@ public SyncCommitteeContribution randomSyncCommitteeContribution( } public LightClientBootstrap randomLightClientBoostrap(final UInt64 slot) { - LightClientBootstrapSchema bootstrapSchema = + final LightClientBootstrapSchema bootstrapSchema = getAltairSchemaDefinitions(slot).getLightClientBootstrapSchema(); - LightClientHeaderSchema headerSchema = + final LightClientHeaderSchema headerSchema = getAltairSchemaDefinitions(slot).getLightClientHeaderSchema(); return bootstrapSchema.create( @@ -2160,8 +2024,9 @@ public LightClientBootstrap randomLightClientBoostrap(final UInt64 slot) { } public LightClientUpdate randomLightClientUpdate(final UInt64 slot) { - LightClientUpdateSchema schema = getAltairSchemaDefinitions(slot).getLightClientUpdateSchema(); - LightClientHeaderSchema headerSchema = + final LightClientUpdateSchema schema = + getAltairSchemaDefinitions(slot).getLightClientUpdateSchema(); + final LightClientHeaderSchema headerSchema = getAltairSchemaDefinitions(slot).getLightClientHeaderSchema(); return schema.create( @@ -2175,7 +2040,7 @@ public LightClientUpdate randomLightClientUpdate(final UInt64 slot) { } public LightClientUpdateResponse randomLightClientUpdateResponse(final UInt64 slot) { - LightClientUpdateResponseSchema schema = + final LightClientUpdateResponseSchema schema = getAltairSchemaDefinitions(slot).getLightClientUpdateResponseSchema(); return schema.create( @@ -2188,6 +2053,23 @@ public Withdrawal randomWithdrawal() { .create(randomUInt64(), randomValidatorIndex(), randomBytes20(), randomUInt64()); } + public DepositReceipt randomDepositReceiptWithValidSignature(final UInt64 index) { + final BLSKeyPair keyPair = randomKeyPair(); + final DepositMessage depositMessage = + new DepositMessage(keyPair.getPublicKey(), randomBytes32(), randomUInt64()); + final Bytes32 domain = computeDepositDomain(); + final Bytes signingRoot = getSigningRoot(depositMessage, domain); + final BLSSignature signature = BLS.sign(keyPair.getSecretKey(), signingRoot); + return getElectraSchemaDefinitions(randomSlot()) + .getDepositReceiptSchema() + .create( + depositMessage.getPubkey(), + depositMessage.getWithdrawalCredentials(), + depositMessage.getAmount(), + signature, + index); + } + public DepositReceipt randomDepositReceipt() { return getElectraSchemaDefinitions(randomSlot()) .getDepositReceiptSchema() @@ -2234,12 +2116,6 @@ public BlsToExecutionChange randomBlsToExecutionChange() { .create(randomValidatorIndex(), randomPublicKey(), randomBytes20()); } - public BlsToExecutionChange randomBlsToExecutionChange(final int validatorIndex) { - return getCapellaSchemaDefinitions(randomSlot()) - .getBlsToExecutionChangeSchema() - .create(UInt64.valueOf(validatorIndex), randomPublicKey(), randomBytes20()); - } - public SszList randomSignedBlsToExecutionChangesList() { final UInt64 slot = randomSlot(); final SszListSchema signedBlsToExecutionChangeSchema = @@ -2331,7 +2207,7 @@ public BlobSidecar randomBlobSidecarForBlock( .build(); } - public List randomBlobs(int count, UInt64 slot) { + public List randomBlobs(final int count, final UInt64 slot) { final List blobs = new ArrayList<>(); final BlobSchema blobSchema = getDenebSchemaDefinitions(slot).getBlobSchema(); for (int i = 0; i < count; i++) { @@ -2389,6 +2265,20 @@ public BlobsBundle randomBlobsBundle() { return randomBlobsBundle(Optional.empty(), randomSlot()); } + public SszList randomSszKZGProofs(final int count) { + return randomSszList( + getDenebSchemaDefinitions(UInt64.ZERO).getBlobsBundleSchema().getProofsSchema(), + this::randomSszKZGProof, + count); + } + + public SszList randomSszBlobs(final int count) { + return randomSszList( + getDenebSchemaDefinitions(UInt64.ZERO).getBlobsBundleSchema().getBlobsSchema(), + this::randomBlob, + count); + } + public BlobsBundle randomBlobsBundle(final int count) { return randomBlobsBundle(Optional.of(count), randomSlot()); } @@ -2555,32 +2445,11 @@ public SszList randomBlobKzgCommitments() { } public SszList randomBlobKzgCommitments(final int count) { - final BlobKzgCommitmentsSchema blobKzgCommitmentsSchema = - SchemaDefinitionsDeneb.required( - spec.forMilestone(SpecMilestone.DENEB).getSchemaDefinitions()) - .getBlobKzgCommitmentsSchema(); - - return randomSszList(blobKzgCommitmentsSchema, count, this::randomSszKZGCommitment); + return randomSszList(getBlobKzgCommitmentsSchema(), count, this::randomSszKZGCommitment); } public SszList emptyBlobKzgCommitments() { - return SchemaDefinitionsDeneb.required( - spec.forMilestone(SpecMilestone.DENEB).getSchemaDefinitions()) - .getBlobKzgCommitmentsSchema() - .of(); - } - - public RewardAndPenaltyDeltas randomRewardAndPenaltyDeltas(final int validatorCount) { - final RewardAndPenaltyDeltas rewardAndPenaltyDeltas = - RewardAndPenaltyDeltas.aggregated(validatorCount); - for (int i = 0; i < validatorCount; i++) { - // We are using the aggregated deltas, so it does not matter what component we use here - final RewardAndPenalty rewardAndPenalty = rewardAndPenaltyDeltas.getDelta(i); - rewardAndPenalty.reward(RewardComponent.HEAD, randomUInt64(1000)); - rewardAndPenalty.penalize(RewardComponent.HEAD, randomUInt64(1000)); - } - - return rewardAndPenaltyDeltas; + return getBlobKzgCommitmentsSchema().of(); } public ExecutionLayerExit randomExecutionLayerExit() { @@ -2646,14 +2515,14 @@ private UInt64 getMaxEffectiveBalance() { return getConstant(SpecConfig::getMaxEffectiveBalance); } - private Bytes32 computeDomain() { + private Bytes32 computeDepositDomain() { final SpecVersion genesisSpec = spec.getGenesisSpec(); final Bytes4 domain = Domain.DEPOSIT; return genesisSpec.miscHelpers().computeDomain(domain); } - private Bytes getSigningRoot(final DepositMessage proofOfPossessionData, final Bytes32 domain) { - return spec.getGenesisSpec().miscHelpers().computeSigningRoot(proofOfPossessionData, domain); + private Bytes getSigningRoot(final Merkleizable object, final Bytes32 domain) { + return spec.getGenesisSpec().miscHelpers().computeSigningRoot(object, domain); } UInt64 computeStartSlotAtEpoch(final UInt64 epoch) { diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPool.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPool.java index 4441a05ba8e..a63e635222e 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPool.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPool.java @@ -41,7 +41,6 @@ import tech.pegasys.teku.spec.datastructures.operations.Attestation; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; /** * Maintains a pool of attestations. Attestations can be retrieved either for inclusion in a block @@ -174,9 +173,7 @@ public synchronized int getSize() { } public synchronized SszList getAttestationsForBlock( - final BeaconState stateAtBlockSlot, - final AttestationForkChecker forkChecker, - final AttestationWorthinessChecker worthinessChecker) { + final BeaconState stateAtBlockSlot, final AttestationForkChecker forkChecker) { final UInt64 currentEpoch = spec.getCurrentEpoch(stateAtBlockSlot); final int previousEpochLimit = spec.getPreviousEpochAttestationCapacity(stateAtBlockSlot); @@ -198,7 +195,6 @@ public synchronized SszList getAttestationsForBlock( .filter(Objects::nonNull) .filter(group -> isValid(stateAtBlockSlot, group.getAttestationData())) .filter(forkChecker::areAttestationsFromCorrectFork) - .filter(group -> worthinessChecker.areAttestationsWorthy(group.getAttestationData())) .flatMap(MatchingDataAttestationGroup::stream) .limit(attestationsSchema.getMaxLength()) .map(ValidatableAttestation::getAttestation) diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPool.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPool.java index 77843cea1e6..beec7875569 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPool.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPool.java @@ -13,6 +13,7 @@ package tech.pegasys.teku.statetransition.blobs; +import com.google.common.annotations.VisibleForTesting; import java.time.Duration; import java.util.Optional; import java.util.Queue; @@ -21,25 +22,31 @@ import org.apache.logging.log4j.Logger; import tech.pegasys.teku.infrastructure.async.AsyncRunner; import tech.pegasys.teku.infrastructure.async.Cancellable; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.datastructures.blocks.SignedBeaconBlock; +import tech.pegasys.teku.spec.datastructures.state.Checkpoint; import tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel; import tech.pegasys.teku.spec.logic.common.statetransition.results.BlockImportResult; import tech.pegasys.teku.statetransition.block.BlockImportChannel.BlockImportAndBroadcastValidationResults; import tech.pegasys.teku.statetransition.block.BlockManager; +import tech.pegasys.teku.storage.api.FinalizedCheckpointChannel; /** * This pool is designed to track chain tips blocks that has been attempted to import but failed due * to data unavailability. These are chain tips only because blocks with unknown parent will fail * with UNKNOWN_BLOCK and will be tracked in the block pendingPool. */ -public class DataUnavailableBlockPool { +public class DataUnavailableBlockPool implements FinalizedCheckpointChannel { + public static final int MAX_CAPACITY = 10; private static final Logger LOG = LogManager.getLogger(); private static final Duration WAIT_BEFORE_RETRY = Duration.ofSeconds(1); // this is a queue of chain tips private final Queue awaitingDataAvailabilityQueue = - new ArrayBlockingQueue<>(10); + new ArrayBlockingQueue<>(MAX_CAPACITY); + private final Spec spec; private final BlockManager blockManager; private final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool; private final AsyncRunner asyncRunner; @@ -49,9 +56,11 @@ public class DataUnavailableBlockPool { private boolean inSync = false; public DataUnavailableBlockPool( + final Spec spec, final BlockManager blockManager, final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool, final AsyncRunner asyncRunner) { + this.spec = spec; this.blockManager = blockManager; this.blockBlobSidecarsTrackersPool = blockBlobSidecarsTrackersPool; this.asyncRunner = asyncRunner; @@ -64,9 +73,11 @@ public synchronized void addDataUnavailableBlock(final SignedBeaconBlock block) boolean wasEmpty = awaitingDataAvailabilityQueue.isEmpty(); if (!awaitingDataAvailabilityQueue.offer(block)) { + final SignedBeaconBlock oldestBlock = awaitingDataAvailabilityQueue.poll(); + awaitingDataAvailabilityQueue.add(block); LOG.info( "Discarding block {} as data unavailable retry pool capacity exceeded", - block::toLogString); + oldestBlock::toLogString); return; } if (wasEmpty) { @@ -74,6 +85,14 @@ public synchronized void addDataUnavailableBlock(final SignedBeaconBlock block) } } + @Override + public synchronized void onNewFinalizedCheckpoint( + final Checkpoint checkpoint, final boolean fromOptimisticBlock) { + final UInt64 latestFinalizedSlot = checkpoint.getEpochStartSlot(spec); + awaitingDataAvailabilityQueue.removeIf( + block -> block.getSlot().isLessThanOrEqualTo(latestFinalizedSlot)); + } + public synchronized void onSyncingStatusChanged(boolean inSync) { this.inSync = inSync; @@ -82,6 +101,11 @@ public synchronized void onSyncingStatusChanged(boolean inSync) { awaitingDataAvailabilityQueue.clear(); } + @VisibleForTesting + boolean containsBlock(final SignedBeaconBlock block) { + return awaitingDataAvailabilityQueue.contains(block); + } + private synchronized void tryToReimport() { if (blockImportInProgress) { diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImpl.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImpl.java index 00e70803a42..e1e92ecf747 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImpl.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImpl.java @@ -272,6 +272,13 @@ public synchronized void onCompletedBlockAndBlobSidecars( totalBlobSidecars += (int) addedBlobs; sizeGauge.set(totalBlobSidecars, GAUGE_BLOB_SIDECARS_LABEL); + if (!blobSidecarsTracker.isCompleted()) { + LOG.error( + "Tracker for block {} is supposed to be completed but it is not. Missing blob sidecars: {}", + block.toLogString(), + blobSidecarsTracker.getMissingBlobSidecars().count()); + } + if (orderedBlobSidecarsTrackers.add(slotAndBlockRoot)) { sizeGauge.set(orderedBlobSidecarsTrackers.size(), GAUGE_BLOB_SIDECARS_TRACKERS_LABEL); } diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AggregateAttestationValidator.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AggregateAttestationValidator.java index 8d623f9ad7c..282ac4474eb 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AggregateAttestationValidator.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/AggregateAttestationValidator.java @@ -142,9 +142,7 @@ private SafeFuture verifyAggregate( final BeaconState state = maybeState.get(); // [REJECT] The aggregate attestation has participants - final IntList attestingIndices = - spec.getAttestingIndices( - state, aggregate.getData(), aggregate.getAggregationBits()); + final IntList attestingIndices = spec.getAttestingIndices(state, aggregate); if (attestingIndices.isEmpty()) { return SafeFuture.completedFuture( reject( diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java index 6f4655a41fd..51537cd041a 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/attestation/AggregatingAttestationPoolTest.java @@ -41,7 +41,6 @@ import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.logic.common.operations.validation.AttestationDataValidator.AttestationInvalidReason; -import tech.pegasys.teku.spec.logic.common.statetransition.attestation.AttestationWorthinessChecker; import tech.pegasys.teku.spec.util.DataStructureUtil; class AggregatingAttestationPoolTest { @@ -59,13 +58,10 @@ class AggregatingAttestationPoolTest { mockSpec, new NoOpMetricsSystem(), DEFAULT_MAXIMUM_ATTESTATION_COUNT); private final AttestationForkChecker forkChecker = mock(AttestationForkChecker.class); - private final AttestationWorthinessChecker worthinessChecker = - mock(AttestationWorthinessChecker.class); @BeforeEach public void setUp() { when(forkChecker.areAttestationsFromCorrectFork(any())).thenReturn(true); - when(worthinessChecker.areAttestationsWorthy(any())).thenReturn(true); when(mockSpec.getPreviousEpochAttestationCapacity(any())).thenReturn(Integer.MAX_VALUE); // Fwd some calls to the real spec when(mockSpec.computeEpochAtSlot(any())) @@ -73,8 +69,6 @@ public void setUp() { when(mockSpec.getSlotsPerEpoch(any())).thenAnswer(i -> spec.getSlotsPerEpoch(i.getArgument(0))); when(mockSpec.getCurrentEpoch(any(BeaconState.class))) .thenAnswer(i -> spec.getCurrentEpoch(i.getArgument(0))); - when(mockSpec.createAttestationWorthinessChecker(any())) - .thenAnswer(i -> spec.createAttestationWorthinessChecker(i.getArgument(0))); when(mockSpec.atSlot(any())).thenAnswer(invocation -> spec.atSlot(invocation.getArgument(0))); } @@ -117,10 +111,7 @@ public void getAttestationsForBlock_shouldReturnEmptyListWhenNoAttestationsAvail final BeaconState stateAtBlockSlot = dataStructureUtil.randomBeaconState(); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) - .isEmpty(); + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)).isEmpty(); } @Test @@ -134,10 +125,7 @@ public void getAttestationsForBlock_shouldNotIncludeAttestationsWhereDataDoesNot final BeaconState stateAtBlockSlot = dataStructureUtil.randomBeaconState(); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) - .isEmpty(); + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)).isEmpty(); } @Test @@ -154,8 +142,7 @@ void getAttestationsForBlock_shouldNotThrowExceptionWhenShufflingSeedIsUnknown() // Now it should just exclude the group from consideration final BeaconState state = dataStructureUtil.randomBeaconState(UInt64.valueOf(2)); final SszList result = - aggregatingPool.getAttestationsForBlock( - state, new AttestationForkChecker(spec, state), worthinessChecker); + aggregatingPool.getAttestationsForBlock(state, new AttestationForkChecker(spec, state)); assertThat(result).isEmpty(); } @@ -174,7 +161,7 @@ public void getAttestationsForBlock_shouldIncludeAttestationsThatPassValidation( when(mockSpec.validateAttestation(state, attestation2.getData())).thenReturn(Optional.empty()); when(mockSpec.validateAttestation(state, attestation3.getData())).thenReturn(Optional.empty()); - assertThat(aggregatingPool.getAttestationsForBlock(state, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(state, forkChecker)) .containsExactlyInAnyOrder(attestation2, attestation3); } @@ -186,9 +173,7 @@ public void getAttestationsForBlock_shouldAggregateAttestationsWhenPossible() { final BeaconState stateAtBlockSlot = dataStructureUtil.randomBeaconState(); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)) .containsExactly(aggregateAttestations(attestation1, attestation2)); } @@ -202,9 +187,7 @@ public void getAttestationsForBlock_shouldIncludeAttestationsWithDifferentData() final BeaconState stateAtBlockSlot = dataStructureUtil.randomBeaconState(ONE); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)) .containsExactlyInAnyOrder(aggregateAttestations(attestation1, attestation2), attestation3); } @@ -222,9 +205,7 @@ void getAttestationsForBlock_shouldIncludeMoreRecentAttestationsFirst() { final BeaconState stateAtBlockSlot = dataStructureUtil.randomBeaconState(UInt64.valueOf(10)); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)) .containsExactly(attestation3, attestation2, attestation1); } @@ -237,7 +218,7 @@ public void getAttestationsForBlock_shouldNotAddMoreAttestationsThanAllowedInBlo // Won't be included because of the 2 attestation limit. addAttestationFromValidators(attestationData, 2); - assertThat(aggregatingPool.getAttestationsForBlock(state, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(state, forkChecker)) .containsExactly(attestation1, attestation2); } @@ -279,9 +260,7 @@ void testPrevEpochLimits(final int prevEpochCapacity) { addAttestationFromValidators(startSlotAtCurrentEpoch.minus(3), 1); addAttestationFromValidators(startSlotAtCurrentEpoch.minus(4), 2); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)) .containsExactlyElementsOf(expectedAttestations); } @@ -300,9 +279,7 @@ public void onSlot_shouldPruneAttestationsMoreThanTwoEpochsBehindCurrentSlot() { aggregatingPool.onSlot( pruneAttestationData.getSlot().plus(ATTESTATION_RETENTION_SLOTS).plus(ONE)); - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(stateAtBlockSlot, forkChecker)) .containsOnly(preserveAttestation); assertThat(aggregatingPool.getSize()).isEqualTo(1); } @@ -390,14 +367,12 @@ void shouldRemoveOldSlotsWhenMaximumNumberOfAttestationsReached() { assertThat(aggregatingPool.getSize()).isEqualTo(5); final BeaconState slot1State = dataStructureUtil.randomBeaconState(ONE); - assertThat(aggregatingPool.getAttestationsForBlock(slot1State, forkChecker, worthinessChecker)) - .isNotEmpty(); + assertThat(aggregatingPool.getAttestationsForBlock(slot1State, forkChecker)).isNotEmpty(); addAttestationFromValidators(attestationData2, 6); // Should drop the slot 0 attestations assertThat(aggregatingPool.getSize()).isEqualTo(4); - assertThat(aggregatingPool.getAttestationsForBlock(slot1State, forkChecker, worthinessChecker)) - .isEmpty(); + assertThat(aggregatingPool.getAttestationsForBlock(slot1State, forkChecker)).isEmpty(); } @Test @@ -413,8 +388,7 @@ void shouldNotRemoveLastSlotEvenWhenMaximumNumberOfAttestationsReached() { assertThat(aggregatingPool.getSize()).isEqualTo(5); final BeaconState slot1State = dataStructureUtil.randomBeaconState(ONE); - assertThat(aggregatingPool.getAttestationsForBlock(slot1State, forkChecker, worthinessChecker)) - .isNotEmpty(); + assertThat(aggregatingPool.getAttestationsForBlock(slot1State, forkChecker)).isNotEmpty(); addAttestationFromValidators(attestationData, 6); // Can't drop anything as we only have one slot. @@ -435,7 +409,7 @@ public void getAttestationsForBlock_shouldNotAddAttestationsFromWrongFork() { .thenReturn(true); final BeaconState state = dataStructureUtil.randomBeaconState(ONE); - assertThat(aggregatingPool.getAttestationsForBlock(state, forkChecker, worthinessChecker)) + assertThat(aggregatingPool.getAttestationsForBlock(state, forkChecker)) .containsExactly(attestation2); } @@ -520,24 +494,6 @@ void onReorg_shouldBeAbleToReadAttestations() { assertThat(aggregatingPool.getSize()).isEqualTo(1); } - @Test - void getAttestationsForBlock_inAltairShouldNotIncludeWorthlessAttestations() { - final AttestationData attestationData1 = dataStructureUtil.randomAttestationData(ZERO); - final AttestationData attestationData2 = dataStructureUtil.randomAttestationData(ZERO); - final BeaconState stateAtBlockSlot = dataStructureUtil.randomBeaconState(ONE); - - addAttestationFromValidators(attestationData1, 1, 2); - Attestation attestation2 = addAttestationFromValidators(attestationData2, 3, 4); - - when(worthinessChecker.areAttestationsWorthy(attestationData1)).thenReturn(false); - when(worthinessChecker.areAttestationsWorthy(attestationData2)).thenReturn(true); - - assertThat( - aggregatingPool.getAttestationsForBlock( - stateAtBlockSlot, forkChecker, worthinessChecker)) - .containsExactly(attestation2); - } - private Attestation addAttestationFromValidators(final UInt64 slot, final int... validators) { return addAttestationFromValidators(dataStructureUtil.randomAttestationData(slot), validators); } diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPoolTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPoolTest.java index dab6cc17cd4..251f7858d62 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPoolTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/blobs/DataUnavailableBlockPoolTest.java @@ -21,8 +21,11 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import static tech.pegasys.teku.spec.datastructures.validator.BroadcastValidationLevel.NOT_REQUIRED; +import static tech.pegasys.teku.statetransition.blobs.DataUnavailableBlockPool.MAX_CAPACITY; +import java.util.List; import java.util.Optional; +import java.util.stream.IntStream; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.async.SafeFuture; @@ -45,7 +48,7 @@ public class DataUnavailableBlockPoolTest { mock(BlockBlobSidecarsTrackersPool.class); private final DataUnavailableBlockPool dataUnavailableBlockPool = - new DataUnavailableBlockPool(blockManager, blockBlobSidecarsTrackersPool, asyncRunner); + new DataUnavailableBlockPool(spec, blockManager, blockBlobSidecarsTrackersPool, asyncRunner); private final SignedBeaconBlock block1 = dataStructureUtil.randomSignedBeaconBlock(); private final SignedBeaconBlock block2 = dataStructureUtil.randomSignedBeaconBlock(); @@ -173,4 +176,46 @@ void shouldCancelIfStartSyncing() { verifyNoInteractions(blockManager); } + + @Test + void shouldPruneBlocksOlderThanFinalizedSlot() { + final SignedBeaconBlock blockAtSlot1 = dataStructureUtil.randomSignedBeaconBlock(1); + final SignedBeaconBlock blockAtSlot2 = dataStructureUtil.randomSignedBeaconBlock(2); + final SignedBeaconBlock blockAtSlot10 = dataStructureUtil.randomSignedBeaconBlock(10); + final SignedBeaconBlock blockAtSlot11 = dataStructureUtil.randomSignedBeaconBlock(11); + dataUnavailableBlockPool.addDataUnavailableBlock(blockAtSlot1); + dataUnavailableBlockPool.addDataUnavailableBlock(blockAtSlot2); + dataUnavailableBlockPool.addDataUnavailableBlock(blockAtSlot10); + dataUnavailableBlockPool.addDataUnavailableBlock(blockAtSlot11); + + dataUnavailableBlockPool.onNewFinalizedCheckpoint(dataStructureUtil.randomCheckpoint(1), false); + + assertThat(dataUnavailableBlockPool.containsBlock(blockAtSlot1)).isFalse(); + assertThat(dataUnavailableBlockPool.containsBlock(blockAtSlot2)).isFalse(); + assertThat(dataUnavailableBlockPool.containsBlock(blockAtSlot10)).isTrue(); + assertThat(dataUnavailableBlockPool.containsBlock(blockAtSlot11)).isTrue(); + + dataUnavailableBlockPool.onNewFinalizedCheckpoint(dataStructureUtil.randomCheckpoint(2), false); + + assertThat(dataUnavailableBlockPool.containsBlock(blockAtSlot10)).isFalse(); + assertThat(dataUnavailableBlockPool.containsBlock(blockAtSlot11)).isFalse(); + } + + @Test + void shouldDiscardOldestWhenFull() { + final List blocks = + IntStream.range(0, MAX_CAPACITY) + .mapToObj(dataStructureUtil::randomSignedBeaconBlock) + .toList(); + blocks.forEach(dataUnavailableBlockPool::addDataUnavailableBlock); + + assertThat(dataUnavailableBlockPool.containsBlock(blocks.get(0))).isTrue(); + + final SignedBeaconBlock newBlock = dataStructureUtil.randomSignedBeaconBlock(); + + dataUnavailableBlockPool.addDataUnavailableBlock(newBlock); + + assertThat(dataUnavailableBlockPool.containsBlock(blocks.get(0))).isFalse(); + assertThat(dataUnavailableBlockPool.containsBlock(newBlock)).isTrue(); + } } diff --git a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImplTest.java b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImplTest.java index 5cbeba193c8..5cefab43d43 100644 --- a/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImplTest.java +++ b/ethereum/statetransition/src/test/java/tech/pegasys/teku/statetransition/util/BlockBlobSidecarsTrackersPoolImplTest.java @@ -40,6 +40,7 @@ import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import tech.pegasys.infrastructure.logging.LogCaptor; import tech.pegasys.teku.infrastructure.async.StubAsyncRunner; import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory; import tech.pegasys.teku.infrastructure.time.StubTimeProvider; @@ -227,6 +228,31 @@ public void onNewBlock_shouldIgnorePreDenebBlocks() { assertBlobSidecarsTrackersCount(0); } + @Test + public void onCompletedBlockAndBlobSidecars_shouldLogWarningWhenNotCompleted() { + try (final LogCaptor logCaptor = LogCaptor.forClass(BlockBlobSidecarsTrackersPoolImpl.class)) { + final SignedBeaconBlock block = dataStructureUtil.randomSignedBeaconBlock(currentSlot); + final int expectedBlobs = + block + .getMessage() + .getBody() + .toVersionDeneb() + .orElseThrow() + .getBlobKzgCommitments() + .size(); + + assertThat(expectedBlobs).isGreaterThan(0); + + blockBlobSidecarsTrackersPool.onCompletedBlockAndBlobSidecars(block, List.of()); + + logCaptor.assertErrorLog( + "Tracker for block " + + block.toLogString() + + " is supposed to be completed but it is not. Missing blob sidecars: " + + expectedBlobs); + } + } + @Test public void onNewBlobSidecarOnNewBlock_addTrackerWithBothBlockAndBlobSidecar() { final SignedBeaconBlock block = dataStructureUtil.randomSignedBeaconBlock(currentSlot); diff --git a/gradle/versions.gradle b/gradle/versions.gradle index 6358082a953..0606defd8d8 100644 --- a/gradle/versions.gradle +++ b/gradle/versions.gradle @@ -1,11 +1,11 @@ dependencyManagement { dependencies { - dependency 'com.fasterxml.jackson.core:jackson-databind:2.16.1' - dependency 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.16.1' - dependency 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.16.1' - dependency 'com.fasterxml.jackson.module:jackson-module-kotlin:2.16.1' + dependency 'com.fasterxml.jackson.core:jackson-databind:2.17.0' + dependency 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.0' + dependency 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml:2.17.0' + dependency 'com.fasterxml.jackson.module:jackson-module-kotlin:2.17.0' - dependencySet(group: 'com.google.errorprone', version: '2.26.0') { + dependencySet(group: 'com.google.errorprone', version: '2.26.1') { entry 'error_prone_annotation' entry 'error_prone_check_api' entry 'error_prone_core' @@ -14,7 +14,7 @@ dependencyManagement { dependency 'tech.pegasys.tools.epchecks:errorprone-checks:1.1.1' - dependency 'com.google.guava:guava:33.0.0-jre' + dependency 'com.google.guava:guava:33.1.0-jre' dependency 'org.jsoup:jsoup:1.17.2' @@ -27,14 +27,14 @@ dependencyManagement { dependency 'info.picocli:picocli:4.7.5' - dependencySet(group: 'io.javalin', version: '6.1.2') { + dependencySet(group: 'io.javalin', version: '6.1.3') { entry 'javalin' entry 'javalin-rendering' } dependency 'io.libp2p:jvm-libp2p:1.1.0-RELEASE' dependency 'tech.pegasys:jblst:0.3.11' - dependency 'tech.pegasys:jc-kzg-4844:0.8.0' + dependency 'tech.pegasys:jc-kzg-4844:1.0.0' dependency 'org.hdrhistogram:HdrHistogram:2.1.12' @@ -42,17 +42,17 @@ dependencyManagement { dependency 'org.mock-server:mockserver-junit-jupiter:5.15.0' - dependencySet(group: 'io.swagger.core.v3', version: '2.2.20') { + dependencySet(group: 'io.swagger.core.v3', version: '2.2.21') { entry 'swagger-parser' entry 'swagger-core' entry 'swagger-models' entry 'swagger-annotations' } // On update don't forget to change version in tech.pegasys.teku.infrastructure.restapi.SwaggerUIBuilder - dependency 'org.webjars:swagger-ui:5.11.8' + dependency 'org.webjars:swagger-ui:5.12.0' dependency 'org.thymeleaf:thymeleaf:3.1.2.RELEASE' - dependency 'io.github.classgraph:classgraph:4.8.165' - dependencySet(group: 'com.github.oshi', version: '6.4.13') { + dependency 'io.github.classgraph:classgraph:4.8.168' + dependencySet(group: 'com.github.oshi', version: '6.5.0') { entry 'oshi-core' entry 'oshi-core-java11' } @@ -62,13 +62,13 @@ dependencyManagement { entry 'netty-codec-http' } - dependencySet(group: 'io.vertx', version: '4.5.4') { + dependencySet(group: 'io.vertx', version: '4.5.5') { entry 'vertx-codegen' entry 'vertx-core' entry 'vertx-unit' entry 'vertx-web' } - dependency 'io.projectreactor:reactor-core:3.6.2' + dependency 'io.projectreactor:reactor-core:3.6.4' dependency 'it.unimi.dsi:fastutil:8.5.12' @@ -98,7 +98,7 @@ dependencyManagement { dependency 'org.assertj:assertj-core:3.25.3' - dependency 'org.awaitility:awaitility:4.2.0' + dependency 'org.awaitility:awaitility:4.2.1' dependencySet(group: 'org.bouncycastle', version: '1.77') { entry 'bcprov-jdk18on' @@ -148,7 +148,7 @@ dependencyManagement { entry('plugin-api') } - dependencySet(group: 'org.testcontainers', version: '1.19.6') { + dependencySet(group: 'org.testcontainers', version: '1.19.7') { entry "testcontainers" entry "junit-jupiter" } @@ -172,6 +172,6 @@ dependencyManagement { entry 'jjwt-jackson' } - dependency 'net.jqwik:jqwik:1.8.3' + dependency 'net.jqwik:jqwik:1.8.4' } } diff --git a/infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/SwaggerUIBuilder.java b/infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/SwaggerUIBuilder.java index a4c7f4fc662..c9bccc9934b 100644 --- a/infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/SwaggerUIBuilder.java +++ b/infrastructure/restapi/src/main/java/tech/pegasys/teku/infrastructure/restapi/SwaggerUIBuilder.java @@ -30,7 +30,7 @@ public class SwaggerUIBuilder { // Version here MUST match `swagger-ui` library version - private static final String SWAGGER_UI_VERSION = "5.11.8"; + private static final String SWAGGER_UI_VERSION = "5.12.0"; private static final String SWAGGER_UI_PATH = "/swagger-ui"; private static final String SWAGGER_HOSTED_PATH = "/webjars/swagger-ui/" + SWAGGER_UI_VERSION; diff --git a/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipConfig.java b/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipConfig.java index 1d085141a56..ec42e2268e1 100644 --- a/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipConfig.java +++ b/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipConfig.java @@ -18,6 +18,7 @@ import java.time.Duration; import java.util.function.Consumer; import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException; +import tech.pegasys.teku.networking.p2p.gossip.config.GossipPeerScoringConfig.DirectPeerManager; /** * Gossip options @@ -32,8 +33,8 @@ public class GossipConfig { private static final int DEFAULT_ADVERTISE = 3; private static final int DEFAULT_HISTORY = 6; static final Duration DEFAULT_HEARTBEAT_INTERVAL = Duration.ofMillis(700); - // After EIP-7045, attestations are valid for up to 2 full epochs, so TTL is 65 slots - // 1115 * HEARTBEAT = 1115 * 0.7 / 12 = 65.125 + // After EIP-7045, attestations are valid for up to 2 full epochs, so TTL is 65 + // slots 1115 * HEARTBEAT = 1115 * 0.7 / 12 = 65.125 static final Duration DEFAULT_SEEN_TTL = DEFAULT_HEARTBEAT_INTERVAL.multipliedBy(1115); private final int d; @@ -215,5 +216,11 @@ public Builder seenTTL(final Duration seenTTL) { this.seenTTL = seenTTL; return this; } + + public Builder directPeerManager(final DirectPeerManager directPeerManager) { + checkNotNull(directPeerManager); + this.scoringConfigBuilder.directPeerManager(directPeerManager); + return this; + } } } diff --git a/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipScoringConfig.java b/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipScoringConfig.java index 38dc914c9f7..b36456d0805 100644 --- a/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipScoringConfig.java +++ b/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/gossip/config/GossipScoringConfig.java @@ -16,7 +16,9 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; +import tech.pegasys.teku.networking.p2p.gossip.config.GossipPeerScoringConfig.DirectPeerManager; /** Gossip scoring config. Contains peer scoring and topic scoring configs. */ public class GossipScoringConfig { @@ -163,5 +165,12 @@ public Builder opportunisticGraftThreshold(final Double opportunisticGraftThresh this.opportunisticGraftThreshold = opportunisticGraftThreshold; return this; } + + public Builder directPeerManager(final DirectPeerManager directPeerManager) { + checkNotNull(directPeerManager); + + this.peerScoringConfigBuilder.directPeerManager(Optional.of(directPeerManager)); + return this; + } } } diff --git a/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfig.java b/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfig.java index ad0a9e37a97..69816bc4210 100644 --- a/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfig.java +++ b/networking/p2p/src/main/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfig.java @@ -21,6 +21,7 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.util.Enumeration; +import java.util.List; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Consumer; @@ -29,6 +30,8 @@ import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException; import tech.pegasys.teku.infrastructure.io.PortAvailability; import tech.pegasys.teku.networking.p2p.gossip.config.GossipConfig; +import tech.pegasys.teku.networking.p2p.gossip.config.GossipPeerScoringConfig.DirectPeerManager; +import tech.pegasys.teku.networking.p2p.peer.NodeId; public class NetworkConfig { @@ -261,5 +264,12 @@ public Builder yamuxEnabled(final boolean yamuxEnabled) { this.yamuxEnabled = yamuxEnabled; return this; } + + public Builder directPeers(final List directPeers) { + checkNotNull(directPeers); + final DirectPeerManager directPeerManager = directPeers::contains; + this.gossipConfigBuilder.directPeerManager(directPeerManager); + return this; + } } } diff --git a/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfigTest.java b/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfigTest.java index 06d7ca368fd..2b0678d738a 100644 --- a/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfigTest.java +++ b/networking/p2p/src/test/java/tech/pegasys/teku/networking/p2p/network/config/NetworkConfigTest.java @@ -15,8 +15,13 @@ import static org.assertj.core.api.Assertions.assertThat; +import io.libp2p.core.multiformats.Multiaddr; +import java.util.List; import java.util.Optional; import org.junit.jupiter.api.Test; +import tech.pegasys.teku.networking.p2p.gossip.config.GossipPeerScoringConfig.DirectPeerManager; +import tech.pegasys.teku.networking.p2p.libp2p.LibP2PNodeId; +import tech.pegasys.teku.networking.p2p.peer.NodeId; @SuppressWarnings("AddressSelection") class NetworkConfigTest { @@ -75,4 +80,32 @@ void checkPrivateKeySourceCreatedCorrectly() { private NetworkConfig createConfig() { return NetworkConfig.builder().advertisedIp(advertisedIp).networkInterface(listenIp).build(); } + + @Test + void checkDirectPeersConfigCreatedCorrectly() { + final String peerAddress1 = + "/ip4/198.51.100.0/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N"; + final String peerAddress2 = + "/ip4/198.51.100.0/tcp/4242/p2p/QmTESTo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjTEST"; + final LibP2PNodeId peerId1 = new LibP2PNodeId(new Multiaddr(peerAddress1).getPeerId()); + final LibP2PNodeId peerId2 = new LibP2PNodeId(new Multiaddr(peerAddress2).getPeerId()); + + final List directPeers = List.of(peerId1); + + final NetworkConfig config = + NetworkConfig.builder() + .advertisedIp(advertisedIp) + .networkInterface(listenIp) + .directPeers(directPeers) + .build(); + + final Optional optionalDirectPeerManager = + config.getGossipConfig().getScoringConfig().getPeerScoringConfig().getDirectPeerManager(); + assertThat(optionalDirectPeerManager).isPresent(); + + final DirectPeerManager manager = optionalDirectPeerManager.get(); + + assert manager.isDirectPeer(peerId1); + assert !manager.isDirectPeer(peerId2); + } } diff --git a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java index 56dd56300e3..3cd0cccd2f2 100644 --- a/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java +++ b/services/beaconchain/src/main/java/tech/pegasys/teku/services/beaconchain/BeaconChainController.java @@ -148,6 +148,7 @@ import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessagePool; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessageValidator; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeStateUtils; +import tech.pegasys.teku.statetransition.util.BlockBlobSidecarsTrackersPoolImpl; import tech.pegasys.teku.statetransition.util.FutureItems; import tech.pegasys.teku.statetransition.util.PendingPool; import tech.pegasys.teku.statetransition.util.PoolFactory; @@ -600,9 +601,12 @@ protected void initBlockPoolsAndCaches() { protected void initBlockBlobSidecarsTrackersPool() { LOG.debug("BeaconChainController.initBlockBlobSidecarsTrackersPool()"); if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { - blockBlobSidecarsTrackersPool = + final BlockBlobSidecarsTrackersPoolImpl pool = poolFactory.createPoolForBlockBlobSidecarsTrackers( spec, timeProvider, beaconAsyncRunner, recentChainData); + eventChannels.subscribe(FinalizedCheckpointChannel.class, pool); + blockBlobSidecarsTrackersPool = pool; + } else { blockBlobSidecarsTrackersPool = BlockBlobSidecarsTrackersPool.NOOP; } @@ -1214,9 +1218,11 @@ public void initBlockManager() { if (spec.isMilestoneSupported(SpecMilestone.DENEB)) { final DataUnavailableBlockPool dataUnavailableBlockPool = new DataUnavailableBlockPool( - blockManager, blockBlobSidecarsTrackersPool, beaconAsyncRunner); + spec, blockManager, blockBlobSidecarsTrackersPool, beaconAsyncRunner); blockManager.subscribeDataUnavailable(dataUnavailableBlockPool::addDataUnavailableBlock); + eventChannels.subscribe(FinalizedCheckpointChannel.class, dataUnavailableBlockPool); + this.dataUnavailableBlockPool = Optional.of(dataUnavailableBlockPool); } eventChannels diff --git a/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfiguration.java b/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfiguration.java index 79e93a6d999..9b6dda98129 100644 --- a/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfiguration.java +++ b/services/executionlayer/src/main/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfiguration.java @@ -14,7 +14,6 @@ package tech.pegasys.teku.services.executionlayer; import static com.google.common.base.Preconditions.checkState; -import static tech.pegasys.teku.ethereum.executionlayer.ExecutionBuilderModule.BUILDER_BOOST_FACTOR_MAX_PROFIT; import static tech.pegasys.teku.ethereum.executionlayer.ExecutionBuilderModule.BUILDER_BOOST_FACTOR_PREFER_BUILDER; import static tech.pegasys.teku.spec.executionlayer.ExecutionLayerChannel.STUB_ENDPOINT_PREFIX; @@ -35,7 +34,7 @@ public class ExecutionLayerConfiguration { public static final int DEFAULT_BUILDER_CIRCUIT_BREAKER_ALLOWED_FAULTS = 5; public static final int DEFAULT_BUILDER_CIRCUIT_BREAKER_ALLOWED_CONSECUTIVE_FAULTS = 3; public static final int BUILDER_CIRCUIT_BREAKER_WINDOW_HARD_CAP = 64; - public static final UInt64 DEFAULT_BUILDER_BID_COMPARE_FACTOR = BUILDER_BOOST_FACTOR_MAX_PROFIT; + public static final UInt64 DEFAULT_BUILDER_BID_COMPARE_FACTOR = UInt64.valueOf(90); public static final boolean DEFAULT_BUILDER_SET_USER_AGENT_HEADER = true; public static final boolean DEFAULT_USE_SHOULD_OVERRIDE_BUILDER_FLAG = true; public static final boolean DEFAULT_EXCHANGE_CAPABILITIES_MONITORING_ENABLED = true; diff --git a/services/executionlayer/src/test/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfigurationTest.java b/services/executionlayer/src/test/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfigurationTest.java index 7cb0c974db7..60b2b132c52 100644 --- a/services/executionlayer/src/test/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfigurationTest.java +++ b/services/executionlayer/src/test/java/tech/pegasys/teku/services/executionlayer/ExecutionLayerConfigurationTest.java @@ -16,9 +16,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; -import static tech.pegasys.teku.ethereum.executionlayer.ExecutionBuilderModule.BUILDER_BOOST_FACTOR_MAX_PROFIT; import static tech.pegasys.teku.ethereum.executionlayer.ExecutionBuilderModule.BUILDER_BOOST_FACTOR_PREFER_BUILDER; import static tech.pegasys.teku.services.executionlayer.ExecutionLayerConfiguration.BUILDER_ALWAYS_KEYWORD; +import static tech.pegasys.teku.services.executionlayer.ExecutionLayerConfiguration.DEFAULT_BUILDER_BID_COMPARE_FACTOR; import org.junit.jupiter.api.Test; import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException; @@ -87,7 +87,7 @@ public void shouldParseBuilderBidCompareFactor() { public void shouldHaveCorrectDefaultBuilderBidCompareFactor() { final ExecutionLayerConfiguration.Builder builder1 = configBuilder.specProvider(bellatrixSpec); assertThat(builder1.build().getBuilderBidCompareFactor()) - .isEqualByComparingTo(BUILDER_BOOST_FACTOR_MAX_PROFIT); + .isEqualByComparingTo(DEFAULT_BUILDER_BID_COMPARE_FACTOR); } @Test diff --git a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/gnosis.ssz b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/gnosis.ssz index 4e06af70ddb..56ff8a0f298 100644 Binary files a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/gnosis.ssz and b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/gnosis.ssz differ diff --git a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/goerli.ssz b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/goerli.ssz index 4278f9a5429..a5c56a9d60e 100644 Binary files a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/goerli.ssz and b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/goerli.ssz differ diff --git a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/holesky.ssz b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/holesky.ssz index 3dd0d65b5b3..42efedf03a8 100644 Binary files a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/holesky.ssz and b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/holesky.ssz differ diff --git a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/mainnet.ssz b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/mainnet.ssz index 7f9991c7210..9f339922fdc 100644 Binary files a/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/mainnet.ssz and b/services/powchain/src/main/resources/tech/pegasys/teku/services/powchain/mainnet.ssz differ diff --git a/teku/src/main/java/tech/pegasys/teku/cli/options/P2POptions.java b/teku/src/main/java/tech/pegasys/teku/cli/options/P2POptions.java index 0cfcb5becdb..3af1824c318 100644 --- a/teku/src/main/java/tech/pegasys/teku/cli/options/P2POptions.java +++ b/teku/src/main/java/tech/pegasys/teku/cli/options/P2POptions.java @@ -26,6 +26,7 @@ import tech.pegasys.teku.config.TekuConfiguration; import tech.pegasys.teku.networking.eth2.P2PConfig; import tech.pegasys.teku.networking.p2p.discovery.DiscoveryConfig; +import tech.pegasys.teku.networking.p2p.libp2p.MultiaddrPeerAddress; import tech.pegasys.teku.networking.p2p.network.config.NetworkConfig; public class P2POptions { @@ -143,11 +144,24 @@ public class P2POptions { @Option( names = {"--p2p-static-peers"}, paramLabel = "", - description = "Static peers", + description = + "Specifies a list of 'static' peers with which to establish and maintain connections", split = ",", arity = "0..*") private List p2pStaticPeers = new ArrayList<>(); + @Option( + names = {"--p2p-direct-peers"}, + paramLabel = "", + description = + """ + Specifies a list of 'direct' peers with which to establish and maintain connections. + Direct peers are static peers with which this node will always exchange full messages, regardless of peer scoring mechanisms. + Such peers will also need to enable you as direct in order to work.""", + split = ",", + arity = "0..*") + private List p2pDirectPeers = new ArrayList<>(); + @Option( names = {"--Xp2p-multipeer-sync-enabled"}, paramLabel = "", @@ -321,6 +335,9 @@ private int getP2pUpperBound() { } public void configure(final TekuConfiguration.Builder builder) { + // From a discovery configuration perspective, direct peers are static peers + p2pStaticPeers.addAll(p2pDirectPeers); + builder .p2p( b -> @@ -362,6 +379,13 @@ public void configure(final TekuConfiguration.Builder builder) { if (p2pAdvertisedPort != null) { n.advertisedPort(OptionalInt.of(p2pAdvertisedPort)); } + if (!p2pDirectPeers.isEmpty()) { + n.directPeers( + p2pDirectPeers.stream() + .map(MultiaddrPeerAddress::fromAddress) + .map(MultiaddrPeerAddress::getId) + .toList()); + } n.networkInterface(p2pInterface) .isEnabled(p2pEnabled) .listenPort(p2pPort) diff --git a/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java b/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java index 7d588896af6..25d07b90d94 100644 --- a/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java +++ b/validator/remote/src/integration-test/java/tech/pegasys/teku/validator/remote/typedef/OkHttpValidatorTypeDefClientTest.java @@ -26,6 +26,7 @@ import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NOT_FOUND; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_NO_CONTENT; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_OK; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; import static tech.pegasys.teku.infrastructure.json.JsonUtil.serialize; import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ONE; import static tech.pegasys.teku.spec.config.SpecConfig.FAR_FUTURE_EPOCH; @@ -35,6 +36,7 @@ import it.unimi.dsi.fastutil.ints.IntList; import it.unimi.dsi.fastutil.ints.IntSet; import java.util.List; +import java.util.Locale; import java.util.Optional; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.RecordedRequest; @@ -146,6 +148,8 @@ void publishesBlindedBlockSszEncoded() throws InterruptedException { final RecordedRequest recordedRequest = mockWebServer.takeRequest(); assertThat(recordedRequest.getBody().readByteArray()) .isEqualTo(signedBeaconBlock.sszSerialize().toArrayUnsafe()); + assertThat(recordedRequest.getHeader(HEADER_CONSENSUS_VERSION)) + .isEqualTo(specMilestone.name().toLowerCase(Locale.ROOT)); } @TestTemplate diff --git a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/AbstractTypeDefRequest.java b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/AbstractTypeDefRequest.java index 17b8cf1b1b4..01f53f70302 100644 --- a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/AbstractTypeDefRequest.java +++ b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/AbstractTypeDefRequest.java @@ -130,11 +130,14 @@ protected Optional postJson( protected Optional postOctetStream( final ValidatorApiMethod apiMethod, final Map urlParams, + final Map headers, final byte[] objectBytes, final ResponseHandler responseHandler) { final HttpUrl.Builder httpUrlBuilder = urlBuilder(apiMethod, urlParams); + final Request.Builder builder = requestBuilder(); + headers.forEach(builder::addHeader); final Request request = - requestBuilder() + builder .url(httpUrlBuilder.build()) .post(RequestBody.create(objectBytes, OCTET_STREAM)) .build(); diff --git a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/RegisterValidatorsRequest.java b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/RegisterValidatorsRequest.java index 979c3167446..fc56ab09c69 100644 --- a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/RegisterValidatorsRequest.java +++ b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/RegisterValidatorsRequest.java @@ -66,6 +66,7 @@ private void sendValidatorRegistrationsAsSsz( postOctetStream( ValidatorApiMethod.REGISTER_VALIDATOR, Collections.emptyMap(), + Collections.emptyMap(), ApiSchemas.SIGNED_VALIDATOR_REGISTRATIONS_SCHEMA .sszSerialize(validatorRegistrations) .toArray(), diff --git a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/SendSignedBlockRequest.java b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/SendSignedBlockRequest.java index 2a09cd480cb..b39d334aad4 100644 --- a/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/SendSignedBlockRequest.java +++ b/validator/remote/src/main/java/tech/pegasys/teku/validator/remote/typedef/handlers/SendSignedBlockRequest.java @@ -14,10 +14,13 @@ package tech.pegasys.teku.validator.remote.typedef.handlers; import static tech.pegasys.teku.infrastructure.http.HttpStatusCodes.SC_UNSUPPORTED_MEDIA_TYPE; +import static tech.pegasys.teku.infrastructure.http.RestApiConstants.HEADER_CONSENSUS_VERSION; import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.SEND_SIGNED_BLINDED_BLOCK; import static tech.pegasys.teku.validator.remote.apiclient.ValidatorApiMethod.SEND_SIGNED_BLOCK; import java.util.Collections; +import java.util.Locale; +import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import okhttp3.HttpUrl; @@ -26,6 +29,7 @@ import okhttp3.Response; import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blocks.SignedBlockContainer; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.validator.api.SendSignedBlockResult; @@ -73,7 +77,9 @@ private SendSignedBlockResult sendSignedBlockAsSszOrFallback( final ValidatorApiMethod apiMethod, final SignedBlockContainer signedBlockContainer, final DeserializableTypeDefinition typeDefinition) { - final SendSignedBlockResult result = sendSignedBlockAsSsz(apiMethod, signedBlockContainer); + final SpecMilestone milestone = spec.atSlot(signedBlockContainer.getSlot()).getMilestone(); + final SendSignedBlockResult result = + sendSignedBlockAsSsz(apiMethod, signedBlockContainer, milestone); if (!result.isPublished() && !preferSszBlockEncoding.get()) { return sendSignedBlockAsJson(apiMethod, signedBlockContainer, typeDefinition); } @@ -81,10 +87,13 @@ private SendSignedBlockResult sendSignedBlockAsSszOrFallback( } private SendSignedBlockResult sendSignedBlockAsSsz( - final ValidatorApiMethod apiMethod, final SignedBlockContainer signedBlockContainer) { + final ValidatorApiMethod apiMethod, + final SignedBlockContainer signedBlockContainer, + final SpecMilestone milestone) { return postOctetStream( apiMethod, Collections.emptyMap(), + Map.of(HEADER_CONSENSUS_VERSION, milestone.name().toLowerCase(Locale.ROOT)), signedBlockContainer.sszSerialize().toArray(), sszResponseHandler) .map(__ -> SendSignedBlockResult.success(signedBlockContainer.getRoot()))