From 3d1744e977a1a107372de35551987e2c7d88fed2 Mon Sep 17 00:00:00 2001 From: Mehdi AOUADI Date: Fri, 24 Jan 2025 18:31:55 +0100 Subject: [PATCH] add inclusion list manager (#9034) --- ...tractDataBackedRestAPIIntegrationTest.java | 6 +- .../tech/pegasys/teku/api/DataProvider.java | 10 +-- .../pegasys/teku/api/NodeDataProvider.java | 10 +-- .../teku/api/NodeDataProviderTest.java | 8 +-- .../inclusionlist/InclusionListManager.java | 69 +++++++++++++++++++ .../inclusionlist/InclusionListPool.java | 41 ----------- .../SignedInclusionListValidator.java | 17 ++++- .../beaconchain/BeaconChainController.java | 19 ++--- 8 files changed, 111 insertions(+), 69 deletions(-) create mode 100644 ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListManager.java delete mode 100644 ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListPool.java diff --git a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/AbstractDataBackedRestAPIIntegrationTest.java b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/AbstractDataBackedRestAPIIntegrationTest.java index 4b90f173184..95ab4580731 100644 --- a/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/AbstractDataBackedRestAPIIntegrationTest.java +++ b/data/beaconrestapi/src/integration-test/java/tech/pegasys/teku/beaconrestapi/AbstractDataBackedRestAPIIntegrationTest.java @@ -80,7 +80,7 @@ import tech.pegasys.teku.statetransition.forkchoice.MergeTransitionBlockValidator; import tech.pegasys.teku.statetransition.forkchoice.NoopForkChoiceNotifier; import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; -import tech.pegasys.teku.statetransition.inclusionlist.InclusionListPool; +import tech.pegasys.teku.statetransition.inclusionlist.InclusionListManager; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.validation.SignedBlsToExecutionChangeValidator; import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorCache; @@ -127,7 +127,7 @@ public abstract class AbstractDataBackedRestAPIIntegrationTest { protected final EventChannels eventChannels = mock(EventChannels.class); protected final AggregatingAttestationPool attestationPool = mock(AggregatingAttestationPool.class); - protected final InclusionListPool inclusionListPool = mock(InclusionListPool.class); + protected final InclusionListManager inclusionListManager = mock(InclusionListManager.class); protected final AttestationManager attestationManager = mock(AttestationManager.class); protected final OperationPool attesterSlashingPool = mock(OperationPool.class); protected final OperationPool proposerSlashingPool = mock(OperationPool.class); @@ -235,7 +235,7 @@ private void setupAndStartRestAPI(final BeaconRestApiConfig config) { .attestationManager(attestationManager) .activeValidatorChannel(activeValidatorChannel) .attestationPool(attestationPool) - .inclusionListPool(inclusionListPool) + .inclusionListPool(inclusionListManager) .attesterSlashingPool(attesterSlashingPool) .proposerSlashingPool(proposerSlashingPool) .voluntaryExitPool(voluntaryExitPool) diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java index eb19a903a56..7986346e486 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/DataProvider.java @@ -29,7 +29,7 @@ import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; -import tech.pegasys.teku.statetransition.inclusionlist.InclusionListPool; +import tech.pegasys.teku.statetransition.inclusionlist.InclusionListManager; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; import tech.pegasys.teku.storage.client.CombinedChainDataClient; @@ -105,7 +105,7 @@ public static class Builder { private SyncService syncService; private ValidatorApiChannel validatorApiChannel; private AggregatingAttestationPool attestationPool; - private InclusionListPool inclusionListPool; + private InclusionListManager inclusionListManager; private BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool; private AttestationManager attestationManager; private ActiveValidatorChannel activeValidatorChannel; @@ -154,8 +154,8 @@ public Builder attestationPool(final AggregatingAttestationPool attestationPool) return this; } - public Builder inclusionListPool(final InclusionListPool inclusionListPool) { - this.inclusionListPool = inclusionListPool; + public Builder inclusionListPool(final InclusionListManager inclusionListManager) { + this.inclusionListManager = inclusionListManager; return this; } @@ -230,7 +230,7 @@ public DataProvider build() { final NodeDataProvider nodeDataProvider = new NodeDataProvider( attestationPool, - inclusionListPool, + inclusionListManager, attesterSlashingPool, proposerSlashingPool, voluntaryExitPool, diff --git a/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java b/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java index c171e1b9c64..3e0757ab7c5 100644 --- a/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java +++ b/data/provider/src/main/java/tech/pegasys/teku/api/NodeDataProvider.java @@ -47,7 +47,7 @@ import tech.pegasys.teku.statetransition.forkchoice.PreparedProposerInfo; import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; import tech.pegasys.teku.statetransition.forkchoice.RegisteredValidatorInfo; -import tech.pegasys.teku.statetransition.inclusionlist.InclusionListPool; +import tech.pegasys.teku.statetransition.inclusionlist.InclusionListManager; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.validation.InternalValidationResult; import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; @@ -57,7 +57,7 @@ public class NodeDataProvider { private static final Logger LOG = LogManager.getLogger(); private final AggregatingAttestationPool attestationPool; - private final InclusionListPool inclusionListPool; + private final InclusionListManager inclusionListManager; private final OperationPool attesterSlashingPool; private final OperationPool proposerSlashingPool; private final OperationPool voluntaryExitPool; @@ -74,7 +74,7 @@ public class NodeDataProvider { public NodeDataProvider( final AggregatingAttestationPool attestationPool, - final InclusionListPool inclusionListPool, + final InclusionListManager inclusionListManager, final OperationPool attesterSlashingsPool, final OperationPool proposerSlashingPool, final OperationPool voluntaryExitPool, @@ -89,7 +89,7 @@ public NodeDataProvider( final RecentChainData recentChainData, final Spec spec) { this.attestationPool = attestationPool; - this.inclusionListPool = inclusionListPool; + this.inclusionListManager = inclusionListManager; this.attesterSlashingPool = attesterSlashingsPool; this.proposerSlashingPool = proposerSlashingPool; this.voluntaryExitPool = voluntaryExitPool; @@ -111,7 +111,7 @@ public List getAttestations( } public List getInclusionLists(final UInt64 slot) { - return inclusionListPool.getInclusionLists(slot); + return inclusionListManager.getInclusionLists(slot); } public ObjectAndMetaData> getAttestationsAndMetaData( diff --git a/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java b/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java index d8dc5cd71f5..5a4630836c0 100644 --- a/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java +++ b/data/provider/src/test/java/tech/pegasys/teku/api/NodeDataProviderTest.java @@ -44,7 +44,7 @@ import tech.pegasys.teku.statetransition.blobs.BlockBlobSidecarsTrackersPool; import tech.pegasys.teku.statetransition.forkchoice.ForkChoiceNotifier; import tech.pegasys.teku.statetransition.forkchoice.ProposersDataManager; -import tech.pegasys.teku.statetransition.inclusionlist.InclusionListPool; +import tech.pegasys.teku.statetransition.inclusionlist.InclusionListManager; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.validation.InternalValidationResult; import tech.pegasys.teku.statetransition.validatorcache.ActiveValidatorChannel; @@ -56,7 +56,7 @@ public class NodeDataProviderTest { private final Spec spec = TestSpecFactory.createMinimalCapella(); private final DataStructureUtil dataStructureUtil = new DataStructureUtil(spec); private final AggregatingAttestationPool attestationPool = mock(AggregatingAttestationPool.class); - private final InclusionListPool inclusionListPool = mock(InclusionListPool.class); + private final InclusionListManager inclusionListManager = mock(InclusionListManager.class); private final BlockBlobSidecarsTrackersPool blockBlobSidecarsTrackersPool = mock(BlockBlobSidecarsTrackersPool.class); private final AttestationManager attestationManager = mock(AttestationManager.class); @@ -83,7 +83,7 @@ public void setup() { provider = new NodeDataProvider( attestationPool, - inclusionListPool, + inclusionListManager, attesterSlashingPool, proposerSlashingPool, voluntaryExitPool, @@ -207,7 +207,7 @@ private Spec setUpMockedSpec() { provider = new NodeDataProvider( attestationPool, - inclusionListPool, + inclusionListManager, attesterSlashingPool, proposerSlashingPool, voluntaryExitPool, diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListManager.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListManager.java new file mode 100644 index 00000000000..05cb9f78d33 --- /dev/null +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListManager.java @@ -0,0 +1,69 @@ +/* + * Copyright Consensys Software Inc., 2025 + * + * 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.statetransition.inclusionlist; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import tech.pegasys.teku.ethereum.events.SlotEventsChannel; +import tech.pegasys.teku.infrastructure.async.SafeFuture; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.datastructures.operations.SignedInclusionList; +import tech.pegasys.teku.statetransition.validation.InternalValidationResult; +import tech.pegasys.teku.statetransition.validation.SignedInclusionListValidator; + +public class InclusionListManager implements SlotEventsChannel { + + private final SignedInclusionListValidator signedInclusionListValidator; + + private final Map> validatorIndexToInclusionLists = + new HashMap<>(); + + public InclusionListManager(final SignedInclusionListValidator signedInclusionListValidator) { + this.signedInclusionListValidator = signedInclusionListValidator; + } + + @Override + public synchronized void onSlot(final UInt64 slot) { + validatorIndexToInclusionLists.clear(); + } + + public void add(final SignedInclusionList signedInclusionList) { + final UInt64 validatorIndex = signedInclusionList.getMessage().getValidatorIndex(); + if (validatorIndexToInclusionLists.containsKey(validatorIndex)) { + validatorIndexToInclusionLists.get(validatorIndex).add(signedInclusionList); + } else { + validatorIndexToInclusionLists.put(validatorIndex, List.of(signedInclusionList)); + } + } + + public SafeFuture addSignedInclusionList( + final SignedInclusionList signedInclusionList, final Optional arrivalTimestamp) { + final SafeFuture validationResult = + signedInclusionListValidator.validate(signedInclusionList, validatorIndexToInclusionLists); + return validationResult.thenApply( + result -> { + if (result.isAccept()) { + add(signedInclusionList); + } + return result; + }); + } + + public List getInclusionLists(final UInt64 slot) { + return Collections.emptyList(); + } +} diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListPool.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListPool.java deleted file mode 100644 index 04c5f667cbb..00000000000 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/inclusionlist/InclusionListPool.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Consensys Software Inc., 2025 - * - * 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.statetransition.inclusionlist; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import tech.pegasys.teku.ethereum.events.SlotEventsChannel; -import tech.pegasys.teku.infrastructure.async.SafeFuture; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.datastructures.operations.SignedInclusionList; -import tech.pegasys.teku.statetransition.validation.InternalValidationResult; - -// TODO EIP7805 implement pool logic -public class InclusionListPool implements SlotEventsChannel { - - @Override - public synchronized void onSlot(final UInt64 slot) {} - - public void add(final SignedInclusionList signedInclusionList) {} - - public SafeFuture addRemote( - final SignedInclusionList signedInclusionList, final Optional arrivalTimestamp) { - return SafeFuture.completedFuture(InternalValidationResult.ACCEPT); - } - - public List getInclusionLists(final UInt64 slot) { - return Collections.emptyList(); - } -} diff --git a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/SignedInclusionListValidator.java b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/SignedInclusionListValidator.java index dd8a4c49886..8c19c2d6d88 100644 --- a/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/SignedInclusionListValidator.java +++ b/ethereum/statetransition/src/main/java/tech/pegasys/teku/statetransition/validation/SignedInclusionListValidator.java @@ -13,6 +13,9 @@ package tech.pegasys.teku.statetransition.validation; +import java.util.Collections; +import java.util.List; +import java.util.Map; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.Spec; @@ -34,7 +37,8 @@ public SignedInclusionListValidator(final Spec spec, final RecentChainData recen } public SafeFuture validate( - final SignedInclusionList signedInclusionList) { + final SignedInclusionList signedInclusionList, + final Map> validatorIndexToInclusionLists) { final InclusionList inclusionList = signedInclusionList.getMessage(); final UInt64 slot = inclusionList.getSlot(); @@ -91,8 +95,15 @@ public SafeFuture validate( /* * [IGNORE] The message is either the first or second valid message received from the validator with index message.validator_index. */ - // TODO EIP7805 add an inclusion list cache to track how many we've received from each validator - // and enforce this rule + if (validatorIndexToInclusionLists + .getOrDefault(inclusionList.getValidatorIndex(), Collections.emptyList()) + .size() + > 2) { + return SafeFuture.completedFuture( + InternalValidationResult.ignore( + "Already received 2 Inclusion Lists from validator with index %d", + inclusionList.getValidatorIndex().intValue())); + } return recentChainData .retrieveStateInEffectAtSlot(slot) 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 da0ff05eabe..56e0a147209 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 @@ -145,7 +145,7 @@ import tech.pegasys.teku.statetransition.forkchoice.TickProcessingPerformance; import tech.pegasys.teku.statetransition.forkchoice.TickProcessor; import tech.pegasys.teku.statetransition.genesis.GenesisHandler; -import tech.pegasys.teku.statetransition.inclusionlist.InclusionListPool; +import tech.pegasys.teku.statetransition.inclusionlist.InclusionListManager; import tech.pegasys.teku.statetransition.synccommittee.SignedContributionAndProofValidator; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeContributionPool; import tech.pegasys.teku.statetransition.synccommittee.SyncCommitteeMessagePool; @@ -167,6 +167,7 @@ import tech.pegasys.teku.statetransition.validation.InternalValidationResult; import tech.pegasys.teku.statetransition.validation.ProposerSlashingValidator; import tech.pegasys.teku.statetransition.validation.SignedBlsToExecutionChangeValidator; +import tech.pegasys.teku.statetransition.validation.SignedInclusionListValidator; import tech.pegasys.teku.statetransition.validation.VoluntaryExitValidator; import tech.pegasys.teku.statetransition.validation.signatures.AggregatingSignatureVerificationService; import tech.pegasys.teku.statetransition.validation.signatures.SignatureVerificationService; @@ -253,7 +254,7 @@ public class BeaconChainController extends Service implements BeaconChainControl protected volatile Eth2P2PNetwork p2pNetwork; protected volatile Optional beaconRestAPI = Optional.empty(); protected volatile AggregatingAttestationPool attestationPool; - protected volatile InclusionListPool inclusionListPool; + protected volatile InclusionListManager inclusionListManager; protected volatile DepositProvider depositProvider; protected volatile SyncService syncService; protected volatile AttestationManager attestationManager; @@ -522,7 +523,7 @@ public void initAll() { initCombinedChainDataClient(); initSignatureVerificationService(); initAttestationPool(); - initInclusionListPool(); + initInclusionListManager(); initAttesterSlashingPool(); initProposerSlashingPool(); initVoluntaryExitPool(); @@ -771,7 +772,7 @@ protected void initDataProvider() { .validatorApiChannel( eventChannels.getPublisher(ValidatorApiChannel.class, beaconAsyncRunner)) .attestationPool(attestationPool) - .inclusionListPool(inclusionListPool) + .inclusionListPool(inclusionListManager) .blockBlobSidecarsTrackersPool(blockBlobSidecarsTrackersPool) .attestationManager(attestationManager) .isLivenessTrackingEnabled(getLivenessTrackingEnabled(beaconConfig)) @@ -1147,7 +1148,7 @@ protected void initP2PNetwork() { .gossipedSignedContributionAndProofProcessor(syncCommitteeContributionPool::addRemote) .gossipedSyncCommitteeMessageProcessor(syncCommitteeMessagePool::addRemote) .gossipedSignedBlsToExecutionChangeProcessor(blsToExecutionChangePool::addRemote) - .gossipedSignedInclusionListProcessor(inclusionListPool::addRemote) + .gossipedSignedInclusionListProcessor(inclusionListManager::addSignedInclusionList) .processedAttestationSubscriptionProvider( attestationManager::subscribeToAttestationsToSend) .metricsSystem(metricsSystem) @@ -1202,10 +1203,12 @@ public void initAttestationPool() { attestationPool::onAttestationsIncludedInBlock); } - protected void initInclusionListPool() { + protected void initInclusionListManager() { LOG.debug("BeaconChainController.initInclusionListPool()"); - inclusionListPool = new InclusionListPool(); - eventChannels.subscribe(SlotEventsChannel.class, inclusionListPool); + final SignedInclusionListValidator signedInclusionListValidator = + new SignedInclusionListValidator(spec, recentChainData); + inclusionListManager = new InclusionListManager(signedInclusionListValidator); + eventChannels.subscribe(SlotEventsChannel.class, inclusionListManager); } public void initRestAPI() {