diff --git a/CHANGELOG.md b/CHANGELOG.md index 17bb7a1045a..6cfc17e9e95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +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. -- Default `--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 +- 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/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/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/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/Spec.java index fe1d2288ff2..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 @@ -934,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/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/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/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/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/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/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/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/util/DataStructureUtil.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/DataStructureUtil.java index 5c6607e14f3..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; @@ -1520,20 +1521,20 @@ public DepositMessage randomDepositMessage(final BLSKeyPair keyPair) { } public DepositMessage randomDepositMessage() { - final BLSKeyPair keyPair = BLSTestUtil.randomKeyPair(nextSeed()); + final BLSKeyPair keyPair = randomKeyPair(); return randomDepositMessage(keyPair); } public DepositData randomDepositData() { - final BLSKeyPair keyPair = BLSTestUtil.randomKeyPair(nextSeed()); - final 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); - final 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() { @@ -2052,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() @@ -2497,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) {