diff --git a/acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/CapellaRemoteSignerAcceptanceTest.java b/acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/CapellaRemoteSignerAcceptanceTest.java index 92fe4144bfa..032080fd4b5 100644 --- a/acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/CapellaRemoteSignerAcceptanceTest.java +++ b/acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/CapellaRemoteSignerAcceptanceTest.java @@ -39,10 +39,10 @@ public class CapellaRemoteSignerAcceptanceTest extends AcceptanceTestBase { private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex"); @Test - void denebWithRemoteSigner() throws Exception { + void capellaWithRemoteSigner() throws Exception { final UInt64 currentTime = new SystemTimeProvider().getTimeInSeconds(); final int genesisTime = - currentTime.intValue() + 10; // genesis in 10 seconds to give node time to start + currentTime.intValue() + 30; // genesis needs added time for nodes to startup final Web3SignerNode web3SignerNode = createWeb3SignerNode( diff --git a/build.gradle b/build.gradle index e0e635a9bc7..ed2abc501eb 100644 --- a/build.gradle +++ b/build.gradle @@ -318,7 +318,7 @@ allprojects { } } -def refTestVersion = 'v1.5.0-alpha.6' +def refTestVersion = 'v1.5.0-alpha.7' def blsRefTestVersion = 'v0.1.2' def slashingProtectionInterchangeRefTestVersion = 'v5.3.0' def refTestBaseUrl = 'https://github.com/ethereum/consensus-spec-tests/releases/download' diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/BeaconStateElectra.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/BeaconStateElectra.json index db6584061d0..a776461b6c6 100644 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/BeaconStateElectra.json +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/BeaconStateElectra.json @@ -1,7 +1,7 @@ { "title" : "BeaconStateElectra", "type" : "object", - "required" : [ "genesis_time", "genesis_validators_root", "slot", "fork", "latest_block_header", "block_roots", "state_roots", "historical_roots", "eth1_data", "eth1_data_votes", "eth1_deposit_index", "validators", "balances", "randao_mixes", "slashings", "previous_epoch_participation", "current_epoch_participation", "justification_bits", "previous_justified_checkpoint", "current_justified_checkpoint", "finalized_checkpoint", "inactivity_scores", "current_sync_committee", "next_sync_committee", "latest_execution_payload_header", "next_withdrawal_index", "next_withdrawal_validator_index", "historical_summaries", "deposit_requests_start_index", "deposit_balance_to_consume", "exit_balance_to_consume", "earliest_exit_epoch", "consolidation_balance_to_consume", "earliest_consolidation_epoch", "pending_balance_deposits", "pending_partial_withdrawals", "pending_consolidations" ], + "required" : [ "genesis_time", "genesis_validators_root", "slot", "fork", "latest_block_header", "block_roots", "state_roots", "historical_roots", "eth1_data", "eth1_data_votes", "eth1_deposit_index", "validators", "balances", "randao_mixes", "slashings", "previous_epoch_participation", "current_epoch_participation", "justification_bits", "previous_justified_checkpoint", "current_justified_checkpoint", "finalized_checkpoint", "inactivity_scores", "current_sync_committee", "next_sync_committee", "latest_execution_payload_header", "next_withdrawal_index", "next_withdrawal_validator_index", "historical_summaries", "deposit_requests_start_index", "deposit_balance_to_consume", "exit_balance_to_consume", "earliest_exit_epoch", "consolidation_balance_to_consume", "earliest_consolidation_epoch", "pending_deposits", "pending_partial_withdrawals", "pending_consolidations" ], "properties" : { "genesis_time" : { "type" : "string", @@ -207,10 +207,10 @@ "example" : "1", "format" : "uint64" }, - "pending_balance_deposits" : { + "pending_deposits" : { "type" : "array", "items" : { - "$ref" : "#/components/schemas/PendingBalanceDeposit" + "$ref" : "#/components/schemas/PendingDeposit" } }, "pending_partial_withdrawals" : { diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/PendingBalanceDeposit.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/PendingBalanceDeposit.json deleted file mode 100644 index 9f93161f54c..00000000000 --- a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/PendingBalanceDeposit.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "title" : "PendingBalanceDeposit", - "type" : "object", - "required" : [ "index", "amount" ], - "properties" : { - "index" : { - "type" : "string", - "description" : "unsigned 64 bit integer", - "example" : "1", - "format" : "uint64" - }, - "amount" : { - "type" : "string", - "description" : "unsigned 64 bit integer", - "example" : "1", - "format" : "uint64" - } - } -} \ No newline at end of file diff --git a/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/PendingDeposit.json b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/PendingDeposit.json new file mode 100644 index 00000000000..27ec4a2f0cf --- /dev/null +++ b/data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/PendingDeposit.json @@ -0,0 +1,43 @@ +{ + "title": "PendingDeposit", + "type": "object", + "required": [ + "pubkey", + "withdrawal_credentials", + "amount", + "signature", + "slot" + ], + "properties": { + "pubkey": { + "type": "string", + "pattern": "^0x[a-fA-F0-9]{2,}$", + "description": "Bytes48 hexadecimal", + "format": "bytes" + }, + "withdrawal_credentials": { + "type": "string", + "description": "Bytes32 hexadecimal", + "example": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2", + "format": "byte" + }, + "amount": { + "type": "string", + "description": "unsigned 64 bit integer", + "example": "1", + "format": "uint64" + }, + "signature": { + "type": "string", + "pattern": "^0x[a-fA-F0-9]{2,}$", + "description": "SSZ hexadecimal", + "format": "bytes" + }, + "slot": { + "type": "string", + "description": "unsigned 64 bit integer", + "example": "1", + "format": "uint64" + } + } +} \ No newline at end of file diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/BeaconStateElectra.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/BeaconStateElectra.java index cff6b4e6d59..0317d461e6b 100644 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/BeaconStateElectra.java +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/BeaconStateElectra.java @@ -73,8 +73,8 @@ public class BeaconStateElectra extends BeaconStateAltair { @JsonProperty("earliest_consolidation_epoch") public final UInt64 earliestConsolidationEpoch; - @JsonProperty("pending_balance_deposits") - public final List pendingBalanceDeposits; + @JsonProperty("pending_deposits") + public final List pendingDeposits; @JsonProperty("pending_partial_withdrawals") public final List pendingPartialWithdrawals; @@ -118,8 +118,7 @@ public BeaconStateElectra( @JsonProperty("earliest_exit_epoch") final UInt64 earliestExitEpoch, @JsonProperty("consolidation_balance_to_consume") final UInt64 consolidationBalanceToConsume, @JsonProperty("earliest_consolidation_epoch") final UInt64 earliestConsolidationEpoch, - @JsonProperty("pending_balance_deposits") - final List pendingBalanceDeposits, + @JsonProperty("pending_deposits") final List pendingDeposits, @JsonProperty("pending_partial_withdrawals") final List pendingPartialWithdrawals, @JsonProperty("pending_consolidations") @@ -159,7 +158,7 @@ public BeaconStateElectra( this.earliestExitEpoch = earliestExitEpoch; this.consolidationBalanceToConsume = consolidationBalanceToConsume; this.earliestConsolidationEpoch = earliestConsolidationEpoch; - this.pendingBalanceDeposits = pendingBalanceDeposits; + this.pendingDeposits = pendingDeposits; this.pendingPartialWithdrawals = pendingPartialWithdrawals; this.pendingConsolidations = pendingConsolidations; } @@ -182,8 +181,7 @@ public BeaconStateElectra(final BeaconState beaconState) { this.earliestExitEpoch = electra.getEarliestExitEpoch(); this.consolidationBalanceToConsume = electra.getConsolidationBalanceToConsume(); this.earliestConsolidationEpoch = electra.getEarliestConsolidationEpoch(); - this.pendingBalanceDeposits = - electra.getPendingBalanceDeposits().stream().map(PendingBalanceDeposit::new).toList(); + this.pendingDeposits = electra.getPendingDeposits().stream().map(PendingDeposit::new).toList(); this.pendingPartialWithdrawals = electra.getPendingPartialWithdrawals().stream().map(PendingPartialWithdrawal::new).toList(); this.pendingConsolidations = @@ -211,7 +209,7 @@ protected void applyAdditionalFields( .getHistoricalSummariesSchema(), BeaconStateSchemaElectra.required( mutableBeaconStateElectra.getBeaconStateSchema()) - .getPendingBalanceDepositsSchema(), + .getPendingDepositsSchema(), BeaconStateSchemaElectra.required( mutableBeaconStateElectra.getBeaconStateSchema()) .getPendingPartialWithdrawalsSchema(), @@ -230,8 +228,8 @@ protected static void applyElectraFields( tech.pegasys.teku.spec.datastructures.state.versions.capella.HistoricalSummary, ?> historicalSummariesSchema, final SszListSchema< - tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit, ?> - pendingBalanceDepositsSchema, + tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit, ?> + pendingDepositsSchema, final SszListSchema< tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal, ?> @@ -261,12 +259,10 @@ protected static void applyElectraFields( state.setEarliestExitEpoch(instance.earliestExitEpoch); state.setConsolidationBalanceToConsume(instance.consolidationBalanceToConsume); state.setEarliestConsolidationEpoch(instance.earliestConsolidationEpoch); - state.setPendingBalanceDeposits( - pendingBalanceDepositsSchema.createFromElements( - instance.pendingBalanceDeposits.stream() - .map( - pendingBalanceDeposit -> - pendingBalanceDeposit.asInternalPendingBalanceDeposit(specVersion)) + state.setPendingDeposits( + pendingDepositsSchema.createFromElements( + instance.pendingDeposits.stream() + .map(pendingDeposit -> pendingDeposit.asInternalPendingDeposit(specVersion)) .toList())); state.setPendingPartialWithdrawals( pendingPartialWithdrawalsSchema.createFromElements( diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/PendingBalanceDeposit.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/PendingBalanceDeposit.java deleted file mode 100644 index 4f6a1b4b3bd..00000000000 --- a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/PendingBalanceDeposit.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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.api.schema.electra; - -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.Optional; -import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.spec.SpecVersion; -import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; - -public class PendingBalanceDeposit { - - @JsonProperty("index") - public final int index; - - @JsonProperty("amount") - public final UInt64 amount; - - public PendingBalanceDeposit( - final @JsonProperty("index") int index, final @JsonProperty("amount") UInt64 amount) { - this.index = index; - this.amount = amount; - } - - public PendingBalanceDeposit( - final tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit - internalPendingBalanceDeposit) { - this.index = internalPendingBalanceDeposit.getIndex(); - this.amount = internalPendingBalanceDeposit.getAmount(); - } - - public tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit - asInternalPendingBalanceDeposit(final SpecVersion spec) { - final Optional schemaDefinitionsElectra = - spec.getSchemaDefinitions().toVersionElectra(); - if (schemaDefinitionsElectra.isEmpty()) { - throw new IllegalArgumentException( - "Could not create PendingBalanceDeposit for pre-electra spec"); - } - return schemaDefinitionsElectra - .get() - .getPendingBalanceDepositSchema() - .create(SszUInt64.of(UInt64.valueOf(this.index)), SszUInt64.of(this.amount)); - } -} diff --git a/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/PendingDeposit.java b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/PendingDeposit.java new file mode 100644 index 00000000000..3c14bfaf062 --- /dev/null +++ b/data/serializer/src/main/java/tech/pegasys/teku/api/schema/electra/PendingDeposit.java @@ -0,0 +1,91 @@ +/* + * 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.api.schema.electra; + +import static tech.pegasys.teku.api.schema.SchemaConstants.DESCRIPTION_BYTES96; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.Optional; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.api.schema.BLSSignature; +import tech.pegasys.teku.bls.BLSPublicKey; +import tech.pegasys.teku.ethereum.execution.types.Eth1Address; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszBytes32; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.SpecVersion; +import tech.pegasys.teku.spec.datastructures.type.SszPublicKey; +import tech.pegasys.teku.spec.datastructures.type.SszSignature; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; + +public class PendingDeposit { + + @JsonProperty("pubkey") + private final BLSPublicKey publicKey; + + @JsonProperty("withdrawal_credentials") + private final Eth1Address withdrawalCredentials; + + @JsonProperty("amount") + public final UInt64 amount; + + @Schema(type = "string", format = "byte", description = DESCRIPTION_BYTES96) + public final BLSSignature signature; + + @JsonProperty("slot") + public final UInt64 slot; + + public PendingDeposit( + final @JsonProperty("pubkey") BLSPublicKey publicKey, + final @JsonProperty("withdrawal_credentials") Eth1Address withdrawalCredentials, + final @JsonProperty("amount") UInt64 amount, + final @JsonProperty("signature") BLSSignature signature, + final @JsonProperty("slot") UInt64 slot) { + this.publicKey = publicKey; + this.withdrawalCredentials = withdrawalCredentials; + this.amount = amount; + this.signature = signature; + this.slot = slot; + } + + public PendingDeposit( + final tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit + internalPendingDeposit) { + this.publicKey = internalPendingDeposit.getPublicKey(); + this.withdrawalCredentials = + Eth1Address.fromBytes(internalPendingDeposit.getWithdrawalCredentials()); + this.amount = internalPendingDeposit.getAmount(); + this.signature = new BLSSignature(internalPendingDeposit.getSignature()); + this.slot = internalPendingDeposit.getSlot(); + } + + public tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit + asInternalPendingDeposit(final SpecVersion spec) { + final Optional schemaDefinitionsElectra = + spec.getSchemaDefinitions().toVersionElectra(); + if (schemaDefinitionsElectra.isEmpty()) { + throw new IllegalArgumentException("Could not create PendingDeposit for pre-electra spec"); + } + return schemaDefinitionsElectra + .get() + .getPendingDepositSchema() + .create( + new SszPublicKey(publicKey), + SszBytes32.of(Bytes32.wrap(withdrawalCredentials.getWrappedBytes())), + SszUInt64.of(amount), + new SszSignature(signature.asInternalBLSSignature()), + SszUInt64.of(slot)); + } +} diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochOperation.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochOperation.java index b5f226a03f9..10fbd73a09e 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochOperation.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochOperation.java @@ -26,7 +26,7 @@ public enum EpochOperation { PROCESS_HISTORICAL_ROOTS_UPDATE, SYNC_COMMITTEE_UPDATES, PROCESS_HISTORICAL_SUMMARIES_UPDATE, - PENDING_BALANCE_DEPOSITS, + PENDING_DEPOSITS, PENDING_CONSOLIDATIONS, INACTIVITY_UPDATES } diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingExecutor.java index 1995f2dd11e..258d9a1f36a 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingExecutor.java @@ -45,7 +45,7 @@ public void executeOperation(final EpochOperation operation, final MutableBeacon epochProcessor.processHistoricalSummariesUpdate(state); case SYNC_COMMITTEE_UPDATES -> epochProcessor.processSyncCommitteeUpdates(state); case INACTIVITY_UPDATES -> processInactivityUpdates(state); - case PENDING_BALANCE_DEPOSITS -> processPendingBalanceDeposits(state); + case PENDING_DEPOSITS -> processPendingDeposits(state); case PENDING_CONSOLIDATIONS -> processPendingConsolidations(state); default -> throw new UnsupportedOperationException( @@ -58,8 +58,8 @@ private void processInactivityUpdates(final MutableBeaconState state) { state, validatorStatusFactory.createValidatorStatuses(state)); } - private void processPendingBalanceDeposits(final MutableBeaconState state) { - epochProcessor.processPendingBalanceDeposits(state); + private void processPendingDeposits(final MutableBeaconState state) { + epochProcessor.processPendingDeposits(state); } private void processPendingConsolidations(final MutableBeaconState state) { diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingTestExecutor.java index 6364d3c6d18..c6d0996219f 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/epoch_processing/EpochProcessingTestExecutor.java @@ -34,8 +34,7 @@ public class EpochProcessingTestExecutor implements TestExecutor { ImmutableMap.builder() .put( "epoch_processing/slashings", - // TODO: Re-enable these tests as part of https://github.com/Consensys/teku/pull/8612 - TestExecutor.IGNORE_TESTS) + new EpochProcessingTestExecutor(EpochOperation.PROCESS_SLASHINGS)) .put( "epoch_processing/registry_updates", new EpochProcessingTestExecutor(EpochOperation.PROCESS_REGISTRY_UPDATES)) @@ -83,8 +82,8 @@ public class EpochProcessingTestExecutor implements TestExecutor { "epoch_processing/pending_consolidations", new EpochProcessingTestExecutor(EpochOperation.PENDING_CONSOLIDATIONS)) .put( - "epoch_processing/pending_balance_deposits", - new EpochProcessingTestExecutor(EpochOperation.PENDING_BALANCE_DEPOSITS)) + "epoch_processing/pending_deposits", + new EpochProcessingTestExecutor(EpochOperation.PENDING_DEPOSITS)) .build(); private final EpochOperation operation; diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/operations/OperationsTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/operations/OperationsTestExecutor.java index df11615cc16..5ac96806316 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/operations/OperationsTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/common/operations/OperationsTestExecutor.java @@ -128,10 +128,13 @@ private enum Operation { "operations/withdrawal_request", new OperationsTestExecutor<>( "withdrawal_request.ssz_snappy", Operation.WITHDRAWAL_REQUEST)) + // TODO re-enable consolidation tests (https://github.com/Consensys/teku/issues/8617) .put( "operations/consolidation_request", - new OperationsTestExecutor<>( - "consolidation_request.ssz_snappy", Operation.CONSOLIDATION_REQUEST)) + // new OperationsTestExecutor<>( + // "consolidation_request.ssz_snappy", + // Operation.CONSOLIDATION_REQUEST)) + TestExecutor.IGNORE_TESTS) .build(); private final String dataFileName; diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/sanity/SanityBlocksTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/sanity/SanityBlocksTestExecutor.java index aa81f5b312c..b3879d84cae 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/sanity/SanityBlocksTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/sanity/SanityBlocksTestExecutor.java @@ -41,8 +41,19 @@ public class SanityBlocksTestExecutor implements TestExecutor { private static final String STATE_ROOT_MISMATCH_ERROR_MESSAGE = "Block state root does NOT match the calculated state root"; + // TODO re-enable tests as part of https://github.com/Consensys/teku/issues/8680 + private static final List IGNORED_TEST_NAMES = + List.of( + "basic_el_withdrawal_request", + "basic_btec_and_el_withdrawal_request_in_same_block", + "cl_exit_and_el_withdrawal_request_in_same_block"); + @Override public void runTest(final TestDefinition testDefinition) throws Exception { + if (IGNORED_TEST_NAMES.contains(testDefinition.getTestName())) { + return; + } + final SanityBlocksMetaData metaData = loadYaml(testDefinition, "meta.yaml", SanityBlocksMetaData.class); final BeaconState preState = loadStateFromSsz(testDefinition, "pre.ssz_snappy"); diff --git a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/ssz_static/SszTestExecutor.java b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/ssz_static/SszTestExecutor.java index 2a48f6cb7b0..e2b515afb3c 100644 --- a/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/ssz_static/SszTestExecutor.java +++ b/eth-reference-tests/src/referenceTest/java/tech/pegasys/teku/reference/phase0/ssz_static/SszTestExecutor.java @@ -205,11 +205,6 @@ public class SszTestExecutor implements TestExecutor { new SszTestExecutor<>( schemas -> SchemaDefinitionsElectra.required(schemas).getConsolidationRequestSchema())) - .put( - "ssz_static/PendingBalanceDeposit", - new SszTestExecutor<>( - schemas -> - SchemaDefinitionsElectra.required(schemas).getPendingBalanceDepositSchema())) .put( "ssz_static/PendingDeposit", new SszTestExecutor<>( diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/ValidatorRegistration.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/ValidatorRegistration.java index 4a7b0802196..56784b04f91 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/ValidatorRegistration.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/ValidatorRegistration.java @@ -24,6 +24,7 @@ public class ValidatorRegistration extends Container4 { + public static final ValidatorRegistrationSchema SSZ_SCHEMA = new ValidatorRegistrationSchema(); protected ValidatorRegistration( final ValidatorRegistrationSchema schema, final TreeNode backingNode) { diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/ForkInfo.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/ForkInfo.java index ae625209707..797f52da6c1 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/ForkInfo.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/ForkInfo.java @@ -13,10 +13,13 @@ package tech.pegasys.teku.spec.datastructures.state; +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BYTES32_TYPE; + import com.google.common.base.MoreObjects; import java.util.Objects; import org.apache.tuweni.bytes.Bytes32; import tech.pegasys.teku.infrastructure.bytes.Bytes4; +import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition; import tech.pegasys.teku.spec.Spec; public class ForkInfo { @@ -63,6 +66,13 @@ public boolean equals(final Object o) { && Objects.equals(genesisValidatorsRoot, forkInfo.genesisValidatorsRoot); } + public static SerializableTypeDefinition getJsonTypeDefinition() { + return SerializableTypeDefinition.object(ForkInfo.class) + .withField("fork", Fork.SSZ_SCHEMA.getJsonTypeDefinition(), ForkInfo::getFork) + .withField("genesis_validators_root", BYTES32_TYPE, ForkInfo::getGenesisValidatorsRoot) + .build(); + } + @Override public int hashCode() { return Objects.hash(fork, genesisValidatorsRoot); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/common/BeaconStateFields.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/common/BeaconStateFields.java index c85ecbc30f9..c4c64b47e9a 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/common/BeaconStateFields.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/common/BeaconStateFields.java @@ -76,7 +76,7 @@ public enum BeaconStateFields implements SszFieldName { EARLIEST_EXIT_EPOCH, CONSOLIDATION_BALANCE_TO_CONSUME, EARLIEST_CONSOLIDATION_EPOCH, - PENDING_BALANCE_DEPOSITS, + PENDING_DEPOSITS, PENDING_PARTIAL_WITHDRAWALS, PENDING_CONSOLIDATIONS; diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateElectra.java index 51a9ddaa67c..164c45160a2 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateElectra.java @@ -19,8 +19,8 @@ import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.EARLIEST_CONSOLIDATION_EPOCH; import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.EARLIEST_EXIT_EPOCH; import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.EXIT_BALANCE_TO_CONSUME; -import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_BALANCE_DEPOSITS; import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_CONSOLIDATIONS; +import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_DEPOSITS; import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_PARTIAL_WITHDRAWALS; import com.google.common.base.MoreObjects; @@ -31,8 +31,8 @@ import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconState; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.deneb.BeaconStateDeneb; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; public interface BeaconStateElectra extends BeaconStateDeneb { @@ -63,7 +63,7 @@ static void describeCustomElectraFields( stringBuilder.add("earliest_exit_epoch", state.getEarliestExitEpoch()); stringBuilder.add("consolidation_balance_to_consume", state.getConsolidationBalanceToConsume()); stringBuilder.add("earliest_consolidation_epoch", state.getEarliestConsolidationEpoch()); - addItems(stringBuilder, "pending_balance_deposits", state.getPendingBalanceDeposits()); + addItems(stringBuilder, "pending_deposits", state.getPendingDeposits()); addItems(stringBuilder, "pending_partial_withdrawals", state.getPendingPartialWithdrawals()); addItems(stringBuilder, "pending_consolidations", state.getPendingConsolidations()); } @@ -114,8 +114,8 @@ default UInt64 getEarliestConsolidationEpoch() { return ((SszUInt64) get(index)).get(); } - default SszList getPendingBalanceDeposits() { - final int index = getSchema().getFieldIndex(PENDING_BALANCE_DEPOSITS); + default SszList getPendingDeposits() { + final int index = getSchema().getFieldIndex(PENDING_DEPOSITS); return getAny(index); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateSchemaElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateSchemaElectra.java index 5b1bd4aa780..3a27a2198ab 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateSchemaElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/beaconstate/versions/electra/BeaconStateSchemaElectra.java @@ -38,8 +38,8 @@ import tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.altair.BeaconStateSchemaAltair; import tech.pegasys.teku.spec.datastructures.state.versions.capella.HistoricalSummary; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; public class BeaconStateSchemaElectra @@ -50,7 +50,7 @@ public class BeaconStateSchemaElectra public static final int EARLIEST_EXIT_EPOCH_INDEX = 31; public static final int CONSOLIDATION_BALANCE_TO_CONSUME_INDEX = 32; public static final int EARLIEST_CONSOLIDATION_EPOCH_INDEX = 33; - public static final int PENDING_BALANCE_DEPOSITS_INDEX = 34; + public static final int PENDING_DEPOSITS_INDEX = 34; public static final int PENDING_PARTIAL_WITHDRAWALS_INDEX = 35; public static final int PENDING_CONSOLIDATIONS_INDEX = 36; @@ -62,8 +62,8 @@ public class BeaconStateSchemaElectra private static List getUniqueFields(final SpecConfig specConfig) { final HistoricalSummary.HistoricalSummarySchema historicalSummarySchema = new HistoricalSummary.HistoricalSummarySchema(); - final PendingBalanceDeposit.PendingBalanceDepositSchema pendingBalanceDepositSchema = - new PendingBalanceDeposit.PendingBalanceDepositSchema(); + final PendingDeposit.PendingDepositSchema pendingDepositSchema = + new PendingDeposit.PendingDepositSchema(); final PendingPartialWithdrawal.PendingPartialWithdrawalSchema pendingPartialWithdrawalSchema = new PendingPartialWithdrawal.PendingPartialWithdrawalSchema(); final SpecConfigElectra specConfigElectra = SpecConfigElectra.required(specConfig); @@ -124,11 +124,11 @@ private static List getUniqueFields(final SpecConfig specConfig) { () -> SszPrimitiveSchemas.UINT64_SCHEMA); final SszField pendingBalanceDepositsField = new SszField( - PENDING_BALANCE_DEPOSITS_INDEX, - BeaconStateFields.PENDING_BALANCE_DEPOSITS, + PENDING_DEPOSITS_INDEX, + BeaconStateFields.PENDING_DEPOSITS, () -> SszListSchema.create( - pendingBalanceDepositSchema, specConfigElectra.getPendingDepositsLimit())); + pendingDepositSchema, specConfigElectra.getPendingDepositsLimit())); final SszField pendingPartialWithdrawalsField = new SszField( PENDING_PARTIAL_WITHDRAWALS_INDEX, @@ -228,9 +228,9 @@ public BeaconStateElectraImpl createFromBackingNode(final TreeNode node) { } @SuppressWarnings("unchecked") - public SszListSchema getPendingBalanceDepositsSchema() { - return (SszListSchema) - getChildSchema(getFieldIndex(BeaconStateFields.PENDING_BALANCE_DEPOSITS)); + public SszListSchema getPendingDepositsSchema() { + return (SszListSchema) + getChildSchema(getFieldIndex(BeaconStateFields.PENDING_DEPOSITS)); } @SuppressWarnings("unchecked") 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 de7a6c1c406..5dd99414be0 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 @@ -13,8 +13,8 @@ package tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra; -import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_BALANCE_DEPOSITS; import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_CONSOLIDATIONS; +import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_DEPOSITS; import static tech.pegasys.teku.spec.datastructures.state.beaconstate.common.BeaconStateFields.PENDING_PARTIAL_WITHDRAWALS; import java.util.Optional; @@ -25,8 +25,8 @@ 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; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; public interface MutableBeaconStateElectra extends MutableBeaconStateDeneb, BeaconStateElectra { @@ -80,15 +80,14 @@ default void setEarliestConsolidationEpoch(final UInt64 earliestConsolidationEpo set(fieldIndex, SszUInt64.of(earliestConsolidationEpoch)); } - default void setPendingBalanceDeposits( - final SszList pendingBalanceDeposits) { - final int fieldIndex = getSchema().getFieldIndex(BeaconStateFields.PENDING_BALANCE_DEPOSITS); - set(fieldIndex, pendingBalanceDeposits); + default void setPendingDeposits(final SszList pendingDeposits) { + final int fieldIndex = getSchema().getFieldIndex(BeaconStateFields.PENDING_DEPOSITS); + set(fieldIndex, pendingDeposits); } @Override - default SszMutableList getPendingBalanceDeposits() { - final int index = getSchema().getFieldIndex(PENDING_BALANCE_DEPOSITS); + default SszMutableList getPendingDeposits() { + final int index = getSchema().getFieldIndex(PENDING_DEPOSITS); return getAnyByRef(index); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/versions/electra/PendingBalanceDeposit.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/versions/electra/PendingBalanceDeposit.java deleted file mode 100644 index 5add848267c..00000000000 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/state/versions/electra/PendingBalanceDeposit.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.state.versions.electra; - -import tech.pegasys.teku.infrastructure.ssz.containers.Container2; -import tech.pegasys.teku.infrastructure.ssz.containers.ContainerSchema2; -import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; -import tech.pegasys.teku.infrastructure.ssz.schema.SszPrimitiveSchemas; -import tech.pegasys.teku.infrastructure.ssz.tree.TreeNode; -import tech.pegasys.teku.infrastructure.unsigned.UInt64; - -public class PendingBalanceDeposit extends Container2 { - public static class PendingBalanceDepositSchema - extends ContainerSchema2 { - - public PendingBalanceDepositSchema() { - super( - "PendingBalanceDeposit", - namedSchema("index", SszPrimitiveSchemas.UINT64_SCHEMA), - namedSchema("amount", SszPrimitiveSchemas.UINT64_SCHEMA)); - } - - @Override - public PendingBalanceDeposit createFromBackingNode(final TreeNode node) { - return new PendingBalanceDeposit(this, node); - } - - public PendingBalanceDeposit create(final SszUInt64 index, final SszUInt64 amount) { - return new PendingBalanceDeposit(this, index, amount); - } - - public SszUInt64 getIndexSchema() { - return (SszUInt64) getFieldSchema0(); - } - - public SszUInt64 getAmountSchema() { - return (SszUInt64) getFieldSchema1(); - } - } - - private PendingBalanceDeposit( - final PendingBalanceDepositSchema type, final TreeNode backingNode) { - super(type, backingNode); - } - - private PendingBalanceDeposit( - final PendingBalanceDepositSchema type, final SszUInt64 index, final SszUInt64 amount) { - super(type, index, amount); - } - - public int getIndex() { - return ((SszUInt64) get(0)).get().intValue(); - } - - public UInt64 getAmount() { - return ((SszUInt64) get(1)).get(); - } -} diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/genesis/GenesisGenerator.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/genesis/GenesisGenerator.java index 096cbd369e7..ba1e27c0373 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/genesis/GenesisGenerator.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/genesis/GenesisGenerator.java @@ -39,8 +39,8 @@ import tech.pegasys.teku.spec.datastructures.state.Validator; 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.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateMutatorsElectra; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; @@ -113,55 +113,32 @@ public void updateCandidateState( processActivation(deposit); } }); + + // Process deposit balance updates if (genesisSpec.getMilestone().isGreaterThanOrEqualTo(SpecMilestone.ELECTRA)) { - // because block processing was made, all deposits will be pending, so at this point - // we need to consume all the pending deposits + final SchemaDefinitionsElectra schemaDefinitionsElectra = + SchemaDefinitionsElectra.required(genesisSpec.getSchemaDefinitions()); + final BeaconStateMutatorsElectra mutatorsElectra = + new BeaconStateMutatorsElectra( + specConfig, + genesisSpec.miscHelpers(), + genesisSpec.beaconStateAccessors(), + schemaDefinitionsElectra); + BeaconStateElectra.required(state) + .getPendingDeposits() + .forEach( + pendingDeposit -> { + mutatorsElectra.increaseBalance( + state, + keyCache.getInt(pendingDeposit.getPublicKey()), + pendingDeposit.getAmount()); + }); MutableBeaconStateElectra.required(state) - .setDepositRequestsStartIndex(UNSET_DEPOSIT_REQUESTS_START_INDEX); - final MutableBeaconStateElectra stateElectra = MutableBeaconStateElectra.required(state); - final List uniqueValidatorIndices = - stateElectra.getPendingBalanceDeposits().stream().toList().stream() - .map(PendingBalanceDeposit::getIndex) - .distinct() - .toList(); - for (int i = 0; i < uniqueValidatorIndices.size(); i++) { - consumePendingBalance(stateElectra, uniqueValidatorIndices.get(i)); - processActivation(uniqueValidatorIndices.get(i)); - } - } - } + .setPendingDeposits( + schemaDefinitionsElectra.getPendingDepositsSchema().createFromElements(List.of())); - private void consumePendingBalance( - final MutableBeaconStateElectra stateElectra, final int validatorIndex) { - final SchemaDefinitionsElectra schemaDefinitionsElectra = - SchemaDefinitionsElectra.required(genesisSpec.getSchemaDefinitions()); - final BeaconStateMutatorsElectra mutatorsElectra = - new BeaconStateMutatorsElectra( - specConfig, - genesisSpec.miscHelpers(), - genesisSpec.beaconStateAccessors(), - schemaDefinitionsElectra); - final List pendingBalanceDeposits = - stateElectra.getPendingBalanceDeposits().asList(); - - final UInt64 depositAmount = - pendingBalanceDeposits.stream() - .filter(z -> z.getIndex() == validatorIndex) - .map(PendingBalanceDeposit::getAmount) - .reduce(UInt64::plus) - .orElse(UInt64.ZERO); - mutatorsElectra.increaseBalance(state, validatorIndex, depositAmount); - if (pendingBalanceDeposits.isEmpty()) { - stateElectra.setPendingBalanceDeposits( - schemaDefinitionsElectra.getPendingBalanceDepositsSchema().createFromElements(List.of())); - } else { - stateElectra.setPendingBalanceDeposits( - schemaDefinitionsElectra - .getPendingBalanceDepositsSchema() - .createFromElements( - pendingBalanceDeposits.stream() - .filter(z -> z.getIndex() != validatorIndex) - .toList())); + // Process activations + keyCache.values().intStream().forEach(this::processActivation); } } 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 9f94ea8944d..aabdc8bacdc 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 @@ -691,6 +691,7 @@ private boolean batchVerifyDepositSignatures(final SszList deposits) { } } + /** process_deposit */ public void processDeposit( final MutableBeaconState state, final Deposit deposit, @@ -727,6 +728,7 @@ public void processDepositWithoutCheckingMerkleProof( signatureAlreadyVerified); } + /** apply_deposit */ public void applyDeposit( final MutableBeaconState state, final BLSPublicKey pubkey, @@ -782,7 +784,7 @@ protected void applyDepositToValidatorIndex( beaconStateMutators.increaseBalance(state, validatorIndex, amount); } - private void handleInvalidDeposit( + protected void handleInvalidDeposit( final BLSPublicKey pubkey, final Optional> maybePubkeyToIndexMap) { LOG.debug("Skipping invalid deposit with pubkey {}", pubkey); @@ -793,6 +795,7 @@ private void handleInvalidDeposit( }); } + /** is_valid_deposit_signature */ protected boolean depositSignatureIsValid( final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, @@ -927,6 +930,14 @@ public void processConsolidationRequests( // No Consolidations until Electra } + @Override + public boolean isValidSwitchToCompoundingRequest( + final BeaconState beaconState, final ConsolidationRequest consolidationRequest) + throws BlockProcessingException { + // No Consolidations until Electra + return false; + } + // Catch generic errors and wrap them in a BlockProcessingException protected void safelyProcess(final BlockProcessingAction action) throws BlockProcessingException { try { diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/BlockProcessor.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/BlockProcessor.java index 43d66bcd048..8976f5e7363 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/BlockProcessor.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/block/BlockProcessor.java @@ -182,6 +182,10 @@ void processConsolidationRequests( MutableBeaconState state, List consolidationRequests) throws BlockProcessingException; + boolean isValidSwitchToCompoundingRequest( + BeaconState beaconState, ConsolidationRequest consolidationRequest) + throws BlockProcessingException; + ExpectedWithdrawals getExpectedWithdrawals(BeaconState preState); default Optional toVersionAltair() { diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/AbstractEpochProcessor.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/AbstractEpochProcessor.java index 96064ca5121..6ca58ced844 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/AbstractEpochProcessor.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/AbstractEpochProcessor.java @@ -104,6 +104,16 @@ public BeaconState processEpoch(final BeaconState preState) throws EpochProcessi protected void processEpoch(final BeaconState preState, final MutableBeaconState state) throws EpochProcessingException { + /* + WARNING: After Electra, it is possible that the validator set is updated within epoch processing + (process_pending_deposits). This means that the validator set in the state can get out of sync with + our validatorStatuses cache. This is not a problem for the current epoch processing, but it can cause + undesired side effects in the future. + + Up until Electra, the only function that uses validatorStatuses after process_pending_deposits is + process_effective_balance_updates, and in this particular case it is ok that we don't have the new validators + in validatorStatuses. + */ final ValidatorStatuses validatorStatuses = validatorStatusFactory.createValidatorStatuses(preState); @@ -122,7 +132,7 @@ protected void processEpoch(final BeaconState preState, final MutableBeaconState processRegistryUpdates(state, validatorStatuses.getStatuses()); processSlashings(state, validatorStatuses); processEth1DataReset(state); - processPendingBalanceDeposits(state); + processPendingDeposits(state); processPendingConsolidations(state); processEffectiveBalanceUpdates(state, validatorStatuses.getStatuses()); processSlashingsReset(state); diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/EpochProcessor.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/EpochProcessor.java index db8f3190b34..5ee5cede085 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/EpochProcessor.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/common/statetransition/epoch/EpochProcessor.java @@ -18,6 +18,7 @@ import tech.pegasys.teku.spec.datastructures.blocks.BlockCheckpoints; 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.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.status.TotalBalances; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.status.ValidatorStatus; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.status.ValidatorStatuses; @@ -100,7 +101,9 @@ void processRegistryUpdates(MutableBeaconState state, List stat void processSyncCommitteeUpdates(MutableBeaconState state); - void processPendingBalanceDeposits(MutableBeaconState state); + void applyPendingDeposits(MutableBeaconState state, PendingDeposit deposit); + + void processPendingDeposits(MutableBeaconState state); void processPendingConsolidations(MutableBeaconState state); } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/epoch/EpochProcessorAltair.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/epoch/EpochProcessorAltair.java index c0fc470d16d..ee3c9fb249e 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/epoch/EpochProcessorAltair.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/altair/statetransition/epoch/EpochProcessorAltair.java @@ -30,6 +30,7 @@ import tech.pegasys.teku.spec.datastructures.state.beaconstate.common.TransitionCaches; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.altair.BeaconStateAltair; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.altair.MutableBeaconStateAltair; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.AbstractEpochProcessor; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.RewardAndPenaltyDeltas; @@ -133,10 +134,19 @@ public void processSyncCommitteeUpdates(final MutableBeaconState genericState) { } @Override - public void processPendingBalanceDeposits(final MutableBeaconState state) {} + public void applyPendingDeposits(final MutableBeaconState state, final PendingDeposit deposit) { + // Nothing to do + } @Override - public void processPendingConsolidations(final MutableBeaconState state) {} + public void processPendingDeposits(final MutableBeaconState state) { + // Nothing to do + } + + @Override + public void processPendingConsolidations(final MutableBeaconState state) { + // Nothing to do + } /** * Replaces the progressive total balances in the state transition caches with an altair one if 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 index c9ae735df72..83ce065d599 100644 --- 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 @@ -14,10 +14,12 @@ package tech.pegasys.teku.spec.logic.versions.electra.block; import static com.google.common.base.Preconditions.checkArgument; +import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; import static tech.pegasys.teku.spec.config.SpecConfig.FAR_FUTURE_EPOCH; import static tech.pegasys.teku.spec.config.SpecConfigElectra.FULL_EXIT_REQUEST_AMOUNT; import it.unimi.dsi.fastutil.ints.IntList; +import it.unimi.dsi.fastutil.objects.Object2IntMap; import java.util.List; import java.util.Optional; import java.util.function.Supplier; @@ -31,9 +33,11 @@ import tech.pegasys.teku.infrastructure.ssz.SszMutableList; import tech.pegasys.teku.infrastructure.ssz.collections.SszBitlist; import tech.pegasys.teku.infrastructure.ssz.primitive.SszByte; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszBytes32; import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.cache.IndexedAttestationCache; +import tech.pegasys.teku.spec.config.SpecConfig; import tech.pegasys.teku.spec.config.SpecConfigElectra; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody; import tech.pegasys.teku.spec.datastructures.blocks.blockbody.versions.electra.BeaconBlockBodyElectra; @@ -44,13 +48,16 @@ import tech.pegasys.teku.spec.datastructures.execution.versions.electra.ExecutionRequests; import tech.pegasys.teku.spec.datastructures.execution.versions.electra.WithdrawalRequest; import tech.pegasys.teku.spec.datastructures.operations.Attestation; +import tech.pegasys.teku.spec.datastructures.operations.Deposit; import tech.pegasys.teku.spec.datastructures.state.Validator; 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.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; +import tech.pegasys.teku.spec.datastructures.type.SszPublicKey; +import tech.pegasys.teku.spec.datastructures.type.SszSignature; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators.ValidatorExitContext; import tech.pegasys.teku.spec.logic.common.helpers.Predicates; import tech.pegasys.teku.spec.logic.common.operations.OperationSignatureVerifier; @@ -62,11 +69,10 @@ 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.logic.versions.electra.helpers.BeaconStateAccessorsElectra; import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateMutatorsElectra; +import tech.pegasys.teku.spec.logic.versions.electra.helpers.MiscHelpersElectra; import tech.pegasys.teku.spec.logic.versions.electra.helpers.PredicatesElectra; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; @@ -77,14 +83,15 @@ public class BlockProcessorElectra extends BlockProcessorDeneb { private final SpecConfigElectra specConfigElectra; private final PredicatesElectra predicatesElectra; private final BeaconStateMutatorsElectra beaconStateMutatorsElectra; + private final BeaconStateAccessorsElectra beaconStateAccessorsElectra; private final SchemaDefinitionsElectra schemaDefinitionsElectra; public BlockProcessorElectra( final SpecConfigElectra specConfig, final Predicates predicates, - final MiscHelpersDeneb miscHelpers, + final MiscHelpersElectra miscHelpers, final SyncCommitteeUtil syncCommitteeUtil, - final BeaconStateAccessorsAltair beaconStateAccessors, + final BeaconStateAccessorsElectra beaconStateAccessors, final BeaconStateMutatorsElectra beaconStateMutators, final OperationSignatureVerifier operationSignatureVerifier, final BeaconStateUtil beaconStateUtil, @@ -108,6 +115,7 @@ public BlockProcessorElectra( this.specConfigElectra = specConfig; this.predicatesElectra = PredicatesElectra.required(predicates); this.beaconStateMutatorsElectra = beaconStateMutators; + this.beaconStateAccessorsElectra = beaconStateAccessors; this.schemaDefinitionsElectra = schemaDefinitions; } @@ -214,7 +222,7 @@ public void processWithdrawalRequests( if (maybeValidatorIndex.isEmpty()) { LOG.debug( "process_withdrawal_request: no matching validator for public key {}", - withdrawalRequest.getValidatorPublicKey()); + withdrawalRequest.getValidatorPublicKey().toAbbreviatedString()); return; } @@ -336,6 +344,8 @@ public void processWithdrawalRequests( public void processDepositRequests( final MutableBeaconState state, final List depositRequests) { final MutableBeaconStateElectra electraState = MutableBeaconStateElectra.required(state); + final SszMutableList pendingDeposits = + MutableBeaconStateElectra.required(state).getPendingDeposits(); for (DepositRequest depositRequest : depositRequests) { // process_deposit_request if (electraState @@ -343,14 +353,17 @@ public void processDepositRequests( .equals(SpecConfigElectra.UNSET_DEPOSIT_REQUESTS_START_INDEX)) { electraState.setDepositRequestsStartIndex(depositRequest.getIndex()); } - applyDeposit( - state, - depositRequest.getPubkey(), - depositRequest.getWithdrawalCredentials(), - depositRequest.getAmount(), - depositRequest.getSignature(), - Optional.empty(), - false); + + final PendingDeposit deposit = + schemaDefinitionsElectra + .getPendingDepositSchema() + .create( + new SszPublicKey(depositRequest.getPubkey()), + SszBytes32.of(depositRequest.getWithdrawalCredentials()), + SszUInt64.of(depositRequest.getAmount()), + new SszSignature(depositRequest.getSignature()), + SszUInt64.of(state.getSlot())); + pendingDeposits.append(deposit); } } @@ -364,7 +377,7 @@ public void processDepositRequests( public void processConsolidationRequests( final MutableBeaconState state, final List consolidationRequests) { LOG.debug( - "process_consolidation_request: {} consolidation request to process from block at " + "process_consolidation_request: {} consolidation requests to process from block at " + "slot {}", consolidationRequests.size(), state.getSlot()); @@ -379,6 +392,27 @@ private void processConsolidationRequest( final UInt64 slot = state.getSlot(); final UInt64 currentEpoch = miscHelpers.computeEpochAtSlot(slot); + if (isValidSwitchToCompoundingRequest(state, consolidationRequest)) { + LOG.debug( + "process_consolidation_request: switching validator {} to compounding address", + consolidationRequest.getSourcePubkey().toAbbreviatedString()); + validatorsUtil + .getValidatorIndex(state, consolidationRequest.getSourcePubkey()) + .ifPresent( + sourceValidatorIndex -> + beaconStateMutatorsElectra.switchToCompoundingValidator( + state, sourceValidatorIndex)); + return; + } + + // Verify that source != target, so a consolidation cannot be used as an exit + if (consolidationRequest.getSourcePubkey().equals(consolidationRequest.getTargetPubkey())) { + LOG.debug( + "process_consolidation_request: source_pubkey and target_pubkey must be different (pubkey = {})", + consolidationRequest.getSourcePubkey().toAbbreviatedString()); + return; + } + // If the pending consolidations queue is full, consolidation requests are ignored if (state.getPendingConsolidations().size() == specConfigElectra.getPendingConsolidationsLimit()) { @@ -388,7 +422,7 @@ private void processConsolidationRequest( // If there is too little available consolidation churn limit, consolidation requests are // ignored - if (BeaconStateAccessorsElectra.required(beaconStateAccessors) + if (beaconStateAccessorsElectra .getConsolidationChurnLimit(state) .isLessThanOrEqualTo(specConfigElectra.getMinActivationBalance())) { LOG.debug("process_consolidation_request: not enough consolidation churn limit available"); @@ -401,7 +435,7 @@ private void processConsolidationRequest( if (maybeSourceValidatorIndex.isEmpty()) { LOG.debug( "process_consolidation_request: source_pubkey {} not found", - consolidationRequest.getSourcePubkey()); + consolidationRequest.getSourcePubkey().toAbbreviatedString()); return; } @@ -411,18 +445,14 @@ private void processConsolidationRequest( if (maybeTargetValidatorIndex.isEmpty()) { LOG.debug( "process_consolidation_request: target_pubkey {} not found", - consolidationRequest.getTargetPubkey()); + consolidationRequest.getTargetPubkey().toAbbreviatedString()); return; } - // Verify that source != target, so a consolidation cannot be used as an exit. - if (maybeSourceValidatorIndex.get().equals(maybeTargetValidatorIndex.get())) { - LOG.debug("process_consolidation_request: source_pubkey and target_pubkey must be different"); - return; - } - - final Validator sourceValidator = state.getValidators().get(maybeSourceValidatorIndex.get()); - final Validator targetValidator = state.getValidators().get(maybeTargetValidatorIndex.get()); + final int sourceValidatorIndex = maybeSourceValidatorIndex.get(); + final Validator sourceValidator = state.getValidators().get(sourceValidatorIndex); + final int targetValidatorIndex = maybeTargetValidatorIndex.get(); + final Validator targetValidator = state.getValidators().get(targetValidatorIndex); // Verify source withdrawal credentials final boolean sourceHasExecutionWithdrawalCredentials = @@ -446,21 +476,25 @@ private void processConsolidationRequest( // Verify the source and the target are active if (!predicatesElectra.isActiveValidator(sourceValidator, currentEpoch)) { - LOG.debug("process_consolidation_request: source validator is inactive"); + LOG.debug( + "process_consolidation_request: source validator {} is inactive", sourceValidatorIndex); return; } if (!predicatesElectra.isActiveValidator(targetValidator, currentEpoch)) { - LOG.debug("process_consolidation_request: target validator is inactive"); + LOG.debug( + "process_consolidation_request: target validator {} is inactive", targetValidatorIndex); return; } // Verify exits for source and target have not been initiated if (!sourceValidator.getExitEpoch().equals(FAR_FUTURE_EPOCH)) { - LOG.debug("process_consolidation_request: source validator is exiting"); + LOG.debug( + "process_consolidation_request: source validator {} is exiting", sourceValidatorIndex); return; } if (!targetValidator.getExitEpoch().equals(FAR_FUTURE_EPOCH)) { - LOG.debug("process_consolidation_request: target validator is exiting"); + LOG.debug( + "process_consolidation_request: target validator {} is exiting", targetValidatorIndex); return; } @@ -474,83 +508,196 @@ private void processConsolidationRequest( state .getValidators() .update( - maybeSourceValidatorIndex.get(), + sourceValidatorIndex, v -> v.withExitEpoch(exitEpoch).withWithdrawableEpoch(withdrawableEpoch)); LOG.debug( "process_consolidation_request: updated validator {} with exit_epoch = {}, withdrawable_epoch = {}", - maybeSourceValidatorIndex.get(), + sourceValidatorIndex, exitEpoch, withdrawableEpoch); final PendingConsolidation pendingConsolidation = new PendingConsolidation( schemaDefinitionsElectra.getPendingConsolidationSchema(), - SszUInt64.of(UInt64.valueOf(maybeSourceValidatorIndex.get())), - SszUInt64.of(UInt64.valueOf(maybeTargetValidatorIndex.get()))); + SszUInt64.of(UInt64.valueOf(sourceValidatorIndex)), + SszUInt64.of(UInt64.valueOf(targetValidatorIndex))); state.getPendingConsolidations().append(pendingConsolidation); + // Churn any target excess active balance of target and raise its max + if (predicatesElectra.hasEth1WithdrawalCredential(targetValidator)) { + beaconStateMutatorsElectra.switchToCompoundingValidator(state, targetValidatorIndex); + } + LOG.debug("process_consolidation_request: created {}", pendingConsolidation); } + /** + * Implements function is_valid_switch_to_compounding_request + * + * @see + */ @Override - protected void applyDepositToValidatorIndex( + public boolean isValidSwitchToCompoundingRequest( + final BeaconState state, final ConsolidationRequest consolidationRequest) { + + // Switch to compounding requires source and target be equal + if (!consolidationRequest.getSourcePubkey().equals(consolidationRequest.getTargetPubkey())) { + return false; + } + + // Verify source_pubkey exists + final Optional maybeSourceValidatorIndex = + validatorsUtil.getValidatorIndex(state, consolidationRequest.getSourcePubkey()); + if (maybeSourceValidatorIndex.isEmpty()) { + return false; + } + + final int sourceValidatorIndex = maybeSourceValidatorIndex.get(); + final Validator sourceValidator = state.getValidators().get(sourceValidatorIndex); + + // Verify request has been authorized + final Eth1Address sourceValidatorExecutionAddress = + Predicates.getExecutionAddressUnchecked(sourceValidator.getWithdrawalCredentials()); + if (!sourceValidatorExecutionAddress.equals( + Eth1Address.fromBytes(consolidationRequest.getSourceAddress().getWrappedBytes()))) { + return false; + } + + // Verify source withdrawal credentials + if (!predicatesElectra.hasEth1WithdrawalCredential(sourceValidator)) { + return false; + } + + // Verify the source is active + final UInt64 currentEpoch = miscHelpers.computeEpochAtSlot(state.getSlot()); + if (!predicatesElectra.isActiveValidator(sourceValidator, currentEpoch)) { + return false; + } + + // Verify exit for source has not been initiated + if (!sourceValidator.getExitEpoch().equals(FAR_FUTURE_EPOCH)) { + return false; + } + + return true; + } + + @Override + public void applyDeposit( final MutableBeaconState state, + final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, - final boolean signatureAlreadyVerified, - final int validatorIndex, final UInt64 amount, - final BLSPublicKey pubkey, - final BLSSignature signature) { - final MutableBeaconStateElectra stateElectra = MutableBeaconStateElectra.required(state); - final SszMutableList pendingBalanceDeposits = - MutableBeaconStateElectra.required(state).getPendingBalanceDeposits(); - pendingBalanceDeposits.append( - schemaDefinitionsElectra - .getPendingBalanceDepositSchema() - .create(SszUInt64.of(UInt64.fromLongBits(validatorIndex)), SszUInt64.of(amount))); - stateElectra.setPendingBalanceDeposits(pendingBalanceDeposits); - if (predicatesElectra.isCompoundingWithdrawalCredential(withdrawalCredentials) - && PredicatesElectra.isEth1WithdrawalCredential( - state.getValidators().get(validatorIndex).getWithdrawalCredentials()) - && (signatureAlreadyVerified - || depositSignatureIsValid(pubkey, withdrawalCredentials, amount, signature))) { - beaconStateMutatorsElectra.switchToCompoundingValidator(stateElectra, validatorIndex); + final BLSSignature signature, + final Optional> maybePubkeyToIndexMap, + final boolean signatureAlreadyVerified) { + + // Find the validator index associated with this deposit, if it exists + final Optional existingIndex = + maybePubkeyToIndexMap + .flatMap( + pubkeyToIndexMap -> { + if (pubkeyToIndexMap.containsKey(pubkey)) { + return Optional.of(pubkeyToIndexMap.getInt(pubkey)); + } else { + pubkeyToIndexMap.put(pubkey, state.getValidators().size()); + return Optional.empty(); + } + }) + .or(() -> validatorsUtil.getValidatorIndex(state, pubkey)); + + if (existingIndex.isEmpty()) { + // This is a new validator + // Verify the deposit signature (proof of possession) which is not checked by the deposit + // contract + if (signatureAlreadyVerified + || depositSignatureIsValid(pubkey, withdrawalCredentials, amount, signature)) { + addValidatorToRegistry(state, pubkey, withdrawalCredentials, ZERO); + final PendingDeposit deposit = + schemaDefinitionsElectra + .getPendingDepositSchema() + .create( + new SszPublicKey(pubkey), + SszBytes32.of(withdrawalCredentials), + SszUInt64.of(amount), + new SszSignature(signature), + SszUInt64.of(SpecConfig.GENESIS_SLOT)); + MutableBeaconStateElectra.required(state).getPendingDeposits().append(deposit); + } else { + handleInvalidDeposit(pubkey, maybePubkeyToIndexMap); + } + } else { + final PendingDeposit deposit = + schemaDefinitionsElectra + .getPendingDepositSchema() + .create( + new SszPublicKey(pubkey), + SszBytes32.of(withdrawalCredentials), + SszUInt64.of(amount), + new SszSignature(signature), + SszUInt64.of(SpecConfig.GENESIS_SLOT)); + MutableBeaconStateElectra.required(state).getPendingDeposits().append(deposit); } } + @Override + public void processDepositWithoutCheckingMerkleProof( + final MutableBeaconState state, + final Deposit deposit, + final Optional> maybePubkeyToIndexMap, + final boolean signatureAlreadyVerified) { + state.setEth1DepositIndex(state.getEth1DepositIndex().plus(UInt64.ONE)); + + applyDeposit( + state, + deposit.getData().getPubkey(), + deposit.getData().getWithdrawalCredentials(), + deposit.getData().getAmount(), + deposit.getData().getSignature(), + maybePubkeyToIndexMap, + signatureAlreadyVerified); + } + + /** add_validator_to_registry */ @Override protected void addValidatorToRegistry( final MutableBeaconState state, final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, final UInt64 amount) { - final Validator validator = getValidatorFromDeposit(pubkey, withdrawalCredentials); - LOG.debug("Adding new validator with index {} to state", state.getValidators().size()); - state.getValidators().append(validator); - int validatorIndex = -1; - for (int i = state.getValidators().size() - 1; i >= 0; i--) { - if (state.getValidators().get(i).getPublicKey().equals(pubkey)) { - validatorIndex = i; - break; - } - } - if (validatorIndex < 0) { - throw new IllegalStateException( - "Could not locate validator " + pubkey + " after adding to state."); - } - final MutableBeaconStateElectra stateElectra = MutableBeaconStateElectra.required(state); + final Validator validator = getValidatorFromDeposit(pubkey, withdrawalCredentials, amount); - stateElectra.getBalances().appendElement(UInt64.ZERO); + final MutableBeaconStateElectra stateElectra = MutableBeaconStateElectra.required(state); + stateElectra.getValidators().append(validator); + stateElectra.getBalances().appendElement(amount); stateElectra.getPreviousEpochParticipation().append(SszByte.ZERO); stateElectra.getCurrentEpochParticipation().append(SszByte.ZERO); stateElectra.getInactivityScores().append(SszUInt64.ZERO); - final SszMutableList pendingBalanceDeposits = - MutableBeaconStateElectra.required(state).getPendingBalanceDeposits(); - pendingBalanceDeposits.append( - schemaDefinitionsElectra - .getPendingBalanceDepositSchema() - .create(SszUInt64.of(UInt64.fromLongBits(validatorIndex)), SszUInt64.of(amount))); - stateElectra.setPendingBalanceDeposits(pendingBalanceDeposits); + } + + @Override + protected Validator getValidatorFromDeposit( + final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, final UInt64 amount) { + final Validator validator = + new Validator( + pubkey, + withdrawalCredentials, + ZERO, + false, + FAR_FUTURE_EPOCH, + FAR_FUTURE_EPOCH, + FAR_FUTURE_EPOCH, + FAR_FUTURE_EPOCH); + + final UInt64 maxEffectiveBalance = + beaconStateAccessorsElectra.getValidatorMaxEffectiveBalance(validator); + final UInt64 validatorEffectiveBalance = + amount + .minusMinZero(amount.mod(specConfig.getEffectiveBalanceIncrement())) + .min(maxEffectiveBalance); + + return validator.withEffectiveBalance(validatorEffectiveBalance); } @Override @@ -565,7 +712,7 @@ protected void assertAttestationValid( final List committeeIndices = attestation.getCommitteeIndicesRequired(); final UInt64 committeeCountPerSlot = - beaconStateAccessors.getCommitteeCountPerSlot( + beaconStateAccessorsElectra.getCommitteeCountPerSlot( state, attestation.getData().getTarget().getEpoch()); final SszBitlist aggregationBits = attestation.getAggregationBits(); final Optional committeeCheckResult = @@ -592,7 +739,7 @@ private Optional checkCommittees( return Optional.of(AttestationInvalidReason.COMMITTEE_INDEX_TOO_HIGH); } final IntList committee = - beaconStateAccessors.getBeaconCommittee(state, slot, committeeIndex); + beaconStateAccessorsElectra.getBeaconCommittee(state, slot, committeeIndex); participantsCount += committee.size(); } if (participantsCount != aggregationBits.size()) { diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectra.java index 2995331eb59..7b9e7b2a1ba 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectra.java @@ -19,6 +19,8 @@ import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.bls.BLSSignature; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszBytes32; import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; @@ -27,7 +29,9 @@ import tech.pegasys.teku.spec.datastructures.state.Validator; import tech.pegasys.teku.spec.datastructures.state.beaconstate.MutableBeaconState; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; +import tech.pegasys.teku.spec.datastructures.type.SszPublicKey; +import tech.pegasys.teku.spec.datastructures.type.SszSignature; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; @@ -180,19 +184,16 @@ public UInt64 computeConsolidationEpochAndUpdateChurn( * @param index validatorIndex */ public void switchToCompoundingValidator(final MutableBeaconStateElectra state, final int index) { - if (PredicatesElectra.isEth1WithdrawalCredential( - state.getValidators().get(index).getWithdrawalCredentials())) { - final byte[] withdrawalCredentialsUpdated = - state.getValidators().get(index).getWithdrawalCredentials().toArray(); - withdrawalCredentialsUpdated[0] = COMPOUNDING_WITHDRAWAL_BYTE; - state - .getValidators() - .update( - index, - validator -> - validator.withWithdrawalCredentials(Bytes32.wrap(withdrawalCredentialsUpdated))); - queueExcessActiveBalance(state, index); - } + final byte[] withdrawalCredentialsUpdated = + state.getValidators().get(index).getWithdrawalCredentials().toArray(); + withdrawalCredentialsUpdated[0] = COMPOUNDING_WITHDRAWAL_BYTE; + state + .getValidators() + .update( + index, + validator -> + validator.withWithdrawalCredentials(Bytes32.wrap(withdrawalCredentialsUpdated))); + queueExcessActiveBalance(state, index); } /** @@ -207,15 +208,21 @@ public void queueExcessActiveBalance( final UInt64 minActivationBalance = specConfigElectra.getMinActivationBalance(); if (balance.isGreaterThan(minActivationBalance)) { - final UInt64 excessBalance = balance.minus(minActivationBalance); + final UInt64 excessBalance = balance.minusMinZero(minActivationBalance); state.getBalances().set(validatorIndex, SszUInt64.of(minActivationBalance)); - final PendingBalanceDeposit pendingBalanceDeposit = + final Validator validator = state.getValidators().get(validatorIndex); + final PendingDeposit deposit = schemaDefinitionsElectra - .getPendingBalanceDepositSchema() + .getPendingDepositSchema() .create( - SszUInt64.of(UInt64.fromLongBits(validatorIndex)), SszUInt64.of(excessBalance)); - state.getPendingBalanceDeposits().append(pendingBalanceDeposit); + new SszPublicKey(validator.getPublicKey()), + SszBytes32.of(validator.getWithdrawalCredentials()), + SszUInt64.of(excessBalance), + new SszSignature(BLSSignature.infinity()), + SszUInt64.of(SpecConfig.GENESIS_SLOT)); + + state.getPendingDeposits().append(deposit); } } @@ -223,26 +230,33 @@ public void queueExcessActiveBalance( * queue_entire_balance_and_reset_validator * * @param state beaconState - * @param index validatorIndex + * @param validatorIndex validatorIndex */ public void queueEntireBalanceAndResetValidator( - final MutableBeaconStateElectra state, final int index) { - final UInt64 balance = state.getBalances().getElement(index); - state.getBalances().set(index, SszUInt64.ZERO); + final MutableBeaconStateElectra state, final int validatorIndex) { + final UInt64 balance = state.getBalances().getElement(validatorIndex); + state.getBalances().set(validatorIndex, SszUInt64.ZERO); state .getValidators() .update( - index, + validatorIndex, validator -> validator - .withActivationEligibilityEpoch(FAR_FUTURE_EPOCH) - .withEffectiveBalance(UInt64.ZERO)); + .withEffectiveBalance(UInt64.ZERO) + .withActivationEligibilityEpoch(FAR_FUTURE_EPOCH)); - final PendingBalanceDeposit deposit = + final Validator validator = state.getValidators().get(validatorIndex); + final PendingDeposit deposit = schemaDefinitionsElectra - .getPendingBalanceDepositSchema() - .create(SszUInt64.of(UInt64.valueOf(index)), SszUInt64.of(balance)); - state.getPendingBalanceDeposits().append(deposit); + .getPendingDepositSchema() + .create( + new SszPublicKey(validator.getPublicKey()), + SszBytes32.of(validator.getWithdrawalCredentials()), + SszUInt64.of(balance), + new SszSignature(BLSSignature.infinity()), + SszUInt64.of(SpecConfig.GENESIS_SLOT)); + + state.getPendingDeposits().append(deposit); } @Override diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/statetransition/epoch/EpochProcessorElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/statetransition/epoch/EpochProcessorElectra.java index ee7c0b570c9..40c4c488339 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/statetransition/epoch/EpochProcessorElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/electra/statetransition/epoch/EpochProcessorElectra.java @@ -15,23 +15,35 @@ import static tech.pegasys.teku.infrastructure.unsigned.UInt64.ZERO; import static tech.pegasys.teku.spec.config.SpecConfig.FAR_FUTURE_EPOCH; +import static tech.pegasys.teku.spec.config.SpecConfig.GENESIS_SLOT; +import static tech.pegasys.teku.spec.logic.common.block.AbstractBlockProcessor.depositSignatureVerifier; import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.function.Supplier; +import java.util.stream.IntStream; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.bls.BLSPublicKey; +import tech.pegasys.teku.bls.impl.BlsException; import tech.pegasys.teku.infrastructure.ssz.SszList; import tech.pegasys.teku.infrastructure.ssz.SszMutableList; import tech.pegasys.teku.infrastructure.ssz.collections.SszUInt64List; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszByte; +import tech.pegasys.teku.infrastructure.ssz.primitive.SszUInt64; import tech.pegasys.teku.infrastructure.time.TimeProvider; import tech.pegasys.teku.infrastructure.unsigned.UInt64; import tech.pegasys.teku.spec.config.SpecConfig; -import tech.pegasys.teku.spec.config.SpecConfigCapella; +import tech.pegasys.teku.spec.config.SpecConfigElectra; +import tech.pegasys.teku.spec.constants.Domain; +import tech.pegasys.teku.spec.datastructures.operations.DepositMessage; import tech.pegasys.teku.spec.datastructures.state.Validator; import tech.pegasys.teku.spec.datastructures.state.beaconstate.BeaconStateCache; import tech.pegasys.teku.spec.datastructures.state.beaconstate.MutableBeaconState; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.status.ValidatorStatus; import tech.pegasys.teku.spec.logic.common.statetransition.epoch.status.ValidatorStatusFactory; @@ -40,10 +52,9 @@ import tech.pegasys.teku.spec.logic.common.util.BeaconStateUtil; 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.helpers.MiscHelpersAltair; import tech.pegasys.teku.spec.logic.versions.capella.statetransition.epoch.EpochProcessorCapella; import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateAccessorsElectra; -import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateMutatorsElectra; +import tech.pegasys.teku.spec.logic.versions.electra.helpers.MiscHelpersElectra; import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; @@ -51,12 +62,11 @@ public class EpochProcessorElectra extends EpochProcessorCapella { private final UInt64 minActivationBalance; private final BeaconStateAccessorsElectra stateAccessorsElectra; - private final BeaconStateMutatorsElectra stateMutatorsElectra; private final SchemaDefinitionsElectra schemaDefinitionsElectra; public EpochProcessorElectra( - final SpecConfigCapella specConfig, - final MiscHelpersAltair miscHelpers, + final SpecConfigElectra specConfig, + final MiscHelpersElectra miscHelpers, final BeaconStateAccessorsAltair beaconStateAccessors, final BeaconStateMutators beaconStateMutators, final ValidatorsUtil validatorsUtil, @@ -77,7 +87,6 @@ public EpochProcessorElectra( this.minActivationBalance = specConfig.toVersionElectra().orElseThrow().getMinActivationBalance(); this.stateAccessorsElectra = BeaconStateAccessorsElectra.required(beaconStateAccessors); - this.stateMutatorsElectra = BeaconStateMutatorsElectra.required(beaconStateMutators); this.schemaDefinitionsElectra = SchemaDefinitionsElectra.required(schemaDefinitions); } @@ -158,7 +167,10 @@ public void processEffectiveBalanceUpdates( final UInt64 hysteresisDownwardMultiplier = specConfig.getHysteresisDownwardMultiplier(); final UInt64 hysteresisQuotient = specConfig.getHysteresisQuotient(); final UInt64 effectiveBalanceIncrement = specConfig.getEffectiveBalanceIncrement(); - for (int index = 0; index < validators.size(); index++) { + // TODO-lucas Before we were using state.getValidators().size() but it can lead to an + // OutOfBoundsException when + // we have new validators being created but not added to ValidatorStatus. + for (int index = 0; index < statuses.size(); index++) { final ValidatorStatus status = statuses.get(index); final UInt64 balance = balances.getElement(index); @@ -186,73 +198,173 @@ public void processEffectiveBalanceUpdates( } } - /** process_pending_balance_deposits */ + /** apply_pending_deposit */ @Override - public void processPendingBalanceDeposits(final MutableBeaconState state) { + public void applyPendingDeposits(final MutableBeaconState state, final PendingDeposit deposit) { + validatorsUtil + .getValidatorIndex(state, deposit.getPublicKey()) + .ifPresentOrElse( + validatorIndex -> + beaconStateMutators.increaseBalance(state, validatorIndex, deposit.getAmount()), + () -> { + if (depositSignatureIsValid(deposit)) { + addValidatorToRegistry( + state, + deposit.getPublicKey(), + deposit.getWithdrawalCredentials(), + deposit.getAmount()); + } + }); + } + + // TODO-lucas Duplicates method depositSignatureIsValid from BlockProcessor + /** is_valid_deposit_signature */ + public boolean depositSignatureIsValid(final PendingDeposit deposit) { + try { + return depositSignatureVerifier.verify( + deposit.getPublicKey(), + computeDepositSigningRoot( + deposit.getPublicKey(), deposit.getWithdrawalCredentials(), deposit.getAmount()), + deposit.getSignature()); + } catch (final BlsException e) { + return false; + } + } + + private Bytes computeDepositSigningRoot( + final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, final UInt64 amount) { + final Bytes32 domain = miscHelpers.computeDomain(Domain.DEPOSIT); + final DepositMessage depositMessage = new DepositMessage(pubkey, withdrawalCredentials, amount); + return miscHelpers.computeSigningRoot(depositMessage, domain); + } + + // TODO-lucas Duplicates method addValidatorToRegistry from BlockProcessor + /** add_validator_to_registry */ + public void addValidatorToRegistry( + final MutableBeaconState state, + final BLSPublicKey pubkey, + final Bytes32 withdrawalCredentials, + final UInt64 amount) { + final Validator validator = getValidatorFromDeposit(pubkey, withdrawalCredentials, amount); + final MutableBeaconStateElectra stateElectra = MutableBeaconStateElectra.required(state); - final UInt64 nextEpoch = stateAccessorsElectra.getCurrentEpoch(state).plus(1L); + stateElectra.getValidators().append(validator); + stateElectra.getBalances().appendElement(amount); + stateElectra.getPreviousEpochParticipation().append(SszByte.ZERO); + stateElectra.getCurrentEpochParticipation().append(SszByte.ZERO); + stateElectra.getInactivityScores().append(SszUInt64.ZERO); + } + + /** get_validator_from_deposit */ + private Validator getValidatorFromDeposit( + final BLSPublicKey pubkey, final Bytes32 withdrawalCredentials, final UInt64 amount) { + final Validator validator = + new Validator( + pubkey, + withdrawalCredentials, + ZERO, + false, + FAR_FUTURE_EPOCH, + FAR_FUTURE_EPOCH, + FAR_FUTURE_EPOCH, + FAR_FUTURE_EPOCH); + + final UInt64 maxEffectiveBalance = + stateAccessorsElectra.getValidatorMaxEffectiveBalance(validator); + final UInt64 validatorEffectiveBalance = + amount + .minusMinZero(amount.mod(specConfig.getEffectiveBalanceIncrement())) + .min(maxEffectiveBalance); + + return validator.withEffectiveBalance(validatorEffectiveBalance); + } + + /** process_pending_deposits */ + @Override + public void processPendingDeposits(final MutableBeaconState state) { + final MutableBeaconStateElectra stateElectra = MutableBeaconStateElectra.required(state); + + final UInt64 nextEpoch = beaconStateAccessors.getCurrentEpoch(state).plus(UInt64.ONE); final UInt64 availableForProcessing = stateElectra .getDepositBalanceToConsume() .plus(stateAccessorsElectra.getActivationExitChurnLimit(stateElectra)); - UInt64 processedAmount = UInt64.ZERO; int nextDepositIndex = 0; - final List depositsToPostpone = new ArrayList<>(); + final List depositsToPostpone = new ArrayList<>(); + boolean isChurnLimitReached = false; + final UInt64 finalizedSlot = + miscHelpers.computeStartSlotAtEpoch(stateElectra.getFinalizedCheckpoint().getEpoch()); + + for (final PendingDeposit deposit : stateElectra.getPendingDeposits()) { + // Do not process deposit requests if Eth1 bridge deposits are not yet applied. + final boolean isDepositRequest = deposit.getSlot().isGreaterThan(GENESIS_SLOT); + final boolean hasPendingEth1BridgeDeposits = + stateElectra + .getEth1DepositIndex() + .isLessThan(stateElectra.getDepositRequestsStartIndex()); + if (isDepositRequest && hasPendingEth1BridgeDeposits) { + break; + } + + // Check if deposit has been finalized, otherwise stop processing + if (deposit.getSlot().isGreaterThan(finalizedSlot)) { + break; + } - final SszList pendingBalanceDeposits = - stateElectra.getPendingBalanceDeposits(); - for (final PendingBalanceDeposit deposit : pendingBalanceDeposits) { - final Validator validator = state.getValidators().get(deposit.getIndex()); + // Check if number of processed deposits has not reached the limit, otherwise, stop processing + if (nextDepositIndex + >= SpecConfigElectra.required(specConfig).getMaxPendingDepositsPerEpoch()) { + break; + } + + final Optional maybeValidatorIndex = + validatorsUtil.getValidatorIndex(state, deposit.getPublicKey()); + boolean isValidatorExited = false; + boolean isValidatorWithdrawn = false; + if (maybeValidatorIndex.isPresent()) { + Validator validator = state.getValidators().get(maybeValidatorIndex.get()); + isValidatorExited = validator.getExitEpoch().isLessThan(FAR_FUTURE_EPOCH); + isValidatorWithdrawn = validator.getWithdrawableEpoch().isLessThan(nextEpoch); + } - if (validator.getExitEpoch().isLessThan(FAR_FUTURE_EPOCH)) { + if (isValidatorWithdrawn) { + // Deposited balance will never become active. Increase balance but do not consume churn + applyPendingDeposits(state, deposit); + } else if (isValidatorExited) { // Validator is exiting, postpone the deposit until after withdrawable epoch - if (nextEpoch.isLessThanOrEqualTo(validator.getWithdrawableEpoch())) { - depositsToPostpone.add(deposit); - } else { - // Deposited balance will never become active. Increase balance but do not consume churn - stateMutatorsElectra.increaseBalance(state, deposit.getIndex(), deposit.getAmount()); - } + depositsToPostpone.add(deposit); } else { - // Validator is not exiting, attempt to process deposit - if (processedAmount.plus(deposit.getAmount()).isGreaterThan(availableForProcessing)) { + // Check if deposit fits in the churn, otherwise, do no more deposit processing in this + // epoch + isChurnLimitReached = + processedAmount.plus(deposit.getAmount()).isGreaterThan(availableForProcessing); + if (isChurnLimitReached) { break; } - - // Deposit fits in the churn, process it. Increase balance and consume churn. - stateMutatorsElectra.increaseBalance(state, deposit.getIndex(), deposit.getAmount()); + // Consume churn and apply deposit processedAmount = processedAmount.plus(deposit.getAmount()); + applyPendingDeposits(state, deposit); } - // Regardless of how the deposit was handled, we move on in the queue. - nextDepositIndex++; + // Regardless of how the deposit was handled, we move on in the queue + nextDepositIndex += 1; } - // Updating state.pending_balance_deposits (removing processed deposits) - if (!pendingBalanceDeposits.isEmpty()) { - final List newList = - pendingBalanceDeposits.asList().subList(nextDepositIndex, pendingBalanceDeposits.size()); - stateElectra.setPendingBalanceDeposits( - schemaDefinitionsElectra.getPendingBalanceDepositsSchema().createFromElements(newList)); + final SszMutableList pendingDeposits = stateElectra.getPendingDeposits(); + final ArrayList newPendingDeposits = new ArrayList<>(); + IntStream.range(nextDepositIndex, pendingDeposits.size()) + .sorted() + .forEach(index -> newPendingDeposits.add(pendingDeposits.get(index))); + newPendingDeposits.addAll(depositsToPostpone); + stateElectra.setPendingDeposits( + schemaDefinitionsElectra.getPendingDepositsSchema().createFromElements(newPendingDeposits)); + + // Accumulate churn only if the churn limit has been hit + if (isChurnLimitReached) { stateElectra.setDepositBalanceToConsume(availableForProcessing.minusMinZero(processedAmount)); - } - - // Updating deposit_balance_to_consume - if (stateElectra.getPendingBalanceDeposits().isEmpty()) { - stateElectra.setDepositBalanceToConsume(UInt64.ZERO); } else { - stateElectra.setDepositBalanceToConsume(availableForProcessing.minusMinZero(processedAmount)); - } - - // Adding postponed deposits to pending_balance_deposits - if (!depositsToPostpone.isEmpty()) { - final ArrayList newPendingDeposits = new ArrayList<>(); - newPendingDeposits.addAll(stateElectra.getPendingBalanceDeposits().asList()); - newPendingDeposits.addAll(depositsToPostpone); - stateElectra.setPendingBalanceDeposits( - schemaDefinitionsElectra - .getPendingBalanceDepositsSchema() - .createFromElements(newPendingDeposits)); + stateElectra.setDepositBalanceToConsume(UInt64.ZERO); } } @@ -276,8 +388,6 @@ public void processPendingConsolidations(final MutableBeaconState state) { break; } - stateMutatorsElectra.switchToCompoundingValidator( - stateElectra, pendingConsolidation.getTargetIndex()); final UInt64 activeBalance = stateAccessorsElectra.getActiveBalance(state, pendingConsolidation.getSourceIndex()); beaconStateMutators.decreaseBalance( diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/EpochProcessorPhase0.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/EpochProcessorPhase0.java index f58cae582c6..4c57bd3b650 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/EpochProcessorPhase0.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/phase0/statetransition/epoch/EpochProcessorPhase0.java @@ -19,6 +19,7 @@ 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.phase0.MutableBeaconStatePhase0; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateAccessors; import tech.pegasys.teku.spec.logic.common.helpers.BeaconStateMutators; import tech.pegasys.teku.spec.logic.common.helpers.MiscHelpers; @@ -86,8 +87,17 @@ public void processSyncCommitteeUpdates(final MutableBeaconState state) { } @Override - public void processPendingBalanceDeposits(final MutableBeaconState state) {} + public void applyPendingDeposits(final MutableBeaconState state, final PendingDeposit deposit) { + // Nothing to do + } + + @Override + public void processPendingDeposits(final MutableBeaconState state) { + // Nothing to do + } @Override - public void processPendingConsolidations(final MutableBeaconState state) {} + public void processPendingConsolidations(final MutableBeaconState state) { + // Nothing to do + } } diff --git a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java index fe064ae6f83..1f730bfad8a 100644 --- a/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java +++ b/ethereum/spec/src/main/java/tech/pegasys/teku/spec/schemas/SchemaDefinitionsElectra.java @@ -60,7 +60,6 @@ import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateSchemaElectra; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; @@ -97,7 +96,6 @@ public class SchemaDefinitionsElectra extends SchemaDefinitionsDeneb { private final WithdrawalRequestSchema withdrawalRequestSchema; private final ConsolidationRequestSchema consolidationRequestSchema; - private final PendingBalanceDeposit.PendingBalanceDepositSchema pendingBalanceDepositSchema; private final PendingDeposit.PendingDepositSchema pendingDepositSchema; private final PendingPartialWithdrawal.PendingPartialWithdrawalSchema @@ -171,7 +169,6 @@ public SchemaDefinitionsElectra(final SchemaRegistry schemaRegistry) { this.depositRequestSchema = DepositRequest.SSZ_SCHEMA; this.withdrawalRequestSchema = WithdrawalRequest.SSZ_SCHEMA; this.consolidationRequestSchema = ConsolidationRequest.SSZ_SCHEMA; - this.pendingBalanceDepositSchema = new PendingBalanceDeposit.PendingBalanceDepositSchema(); this.pendingDepositSchema = new PendingDeposit.PendingDepositSchema(); this.pendingPartialWithdrawalSchema = new PendingPartialWithdrawal.PendingPartialWithdrawalSchema(); @@ -320,16 +317,12 @@ public WithdrawalRequestSchema getWithdrawalRequestSchema() { return withdrawalRequestSchema; } - public PendingBalanceDeposit.PendingBalanceDepositSchema getPendingBalanceDepositSchema() { - return pendingBalanceDepositSchema; - } - public PendingDeposit.PendingDepositSchema getPendingDepositSchema() { return pendingDepositSchema; } - public SszListSchema getPendingBalanceDepositsSchema() { - return beaconStateSchema.getPendingBalanceDepositsSchema(); + public SszListSchema getPendingDepositsSchema() { + return beaconStateSchema.getPendingDepositsSchema(); } public SszListSchema getPendingConsolidationsSchema() { diff --git a/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/datastructures/state/PendingBalanceDepositPropertyTest.java b/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/datastructures/state/PendingDepositPropertyTest.java similarity index 72% rename from ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/datastructures/state/PendingBalanceDepositPropertyTest.java rename to ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/datastructures/state/PendingDepositPropertyTest.java index a2d214b3d7b..d77c2272a8f 100644 --- a/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/datastructures/state/PendingBalanceDepositPropertyTest.java +++ b/ethereum/spec/src/property-test/java/tech/pegasys/teku/spec/datastructures/state/PendingDepositPropertyTest.java @@ -19,23 +19,21 @@ import com.fasterxml.jackson.core.JsonProcessingException; import net.jqwik.api.ForAll; import net.jqwik.api.Property; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; -import tech.pegasys.teku.spec.propertytest.suppliers.state.PendingBalanceDepositSupplier; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; +import tech.pegasys.teku.spec.propertytest.suppliers.state.PendingDepositSupplier; -public class PendingBalanceDepositPropertyTest { +public class PendingDepositPropertyTest { @Property void roundTrip( - @ForAll(supplier = PendingBalanceDepositSupplier.class) - final PendingBalanceDeposit pendingBalanceDeposit) + @ForAll(supplier = PendingDepositSupplier.class) final PendingDeposit pendingDeposit) throws JsonProcessingException { - assertRoundTrip(pendingBalanceDeposit); + assertRoundTrip(pendingDeposit); } @Property void deserializeMutated( - @ForAll(supplier = PendingBalanceDepositSupplier.class) - final PendingBalanceDeposit pendingBalanceDeposit, + @ForAll(supplier = PendingDepositSupplier.class) final PendingDeposit pendingDeposit, @ForAll final int seed) { - assertDeserializeMutatedThrowsExpected(pendingBalanceDeposit, seed); + assertDeserializeMutatedThrowsExpected(pendingDeposit, seed); } } 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 index 03bf5679cb4..9d3dd4b9500 100644 --- 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 @@ -140,8 +140,7 @@ public void processesDepositRequests() { assertThat(state.getDepositRequestsStartIndex()) .isEqualTo(UInt64.valueOf(firstElectraDepositRequestIndex)); // verify validators have been added to the state - assertThat(state.getValidators().size()) - .isEqualTo(firstElectraDepositRequestIndex + depositRequestsCount); + assertThat(state.getPendingDeposits()).hasSize(depositRequestsCount); } @Test @@ -429,10 +428,9 @@ public void processDepositTopsUpValidatorBalanceWhenPubkeyIsFoundInRegistry() assertEquals(knownValidator, postState.getValidators().get(originalValidatorRegistrySize - 1)); // as of electra, the balance increase is in the queue, and yet to be applied to the validator. assertEquals(amount, postState.getBalances().getElement(originalValidatorBalancesSize - 1)); - assertThat(BeaconStateElectra.required(postState).getPendingBalanceDeposits().get(0).getIndex()) - .isEqualTo(originalValidatorBalancesSize - 1); - assertThat( - BeaconStateElectra.required(postState).getPendingBalanceDeposits().get(0).getAmount()) + assertThat(BeaconStateElectra.required(postState).getPendingDeposits().get(0).getPublicKey()) + .isEqualTo(postState.getValidators().get(originalValidatorBalancesSize - 1).getPublicKey()); + assertThat(BeaconStateElectra.required(postState).getPendingDeposits().get(0).getAmount()) .isEqualTo(UInt64.THIRTY_TWO_ETH); } diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/forktransition/ElectraStateUpgradeTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/forktransition/ElectraStateUpgradeTest.java index 5161a292afa..a4b1f00a64d 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/forktransition/ElectraStateUpgradeTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/forktransition/ElectraStateUpgradeTest.java @@ -26,7 +26,7 @@ import tech.pegasys.teku.spec.datastructures.state.Validator; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.deneb.BeaconStateDeneb; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateAccessorsElectra; import tech.pegasys.teku.spec.logic.versions.electra.helpers.BeaconStateMutatorsElectra; import tech.pegasys.teku.spec.logic.versions.electra.helpers.MiscHelpersElectra; @@ -82,7 +82,7 @@ void canUpgradeFromDeneb() { assertThat(post.getConsolidationBalanceToConsume()).isEqualTo(UInt64.ZERO); // 80_000/8 (slots -> epochs) + max_seed_lookahead + 1 assertThat(post.getEarliestConsolidationEpoch()).isEqualTo(UInt64.valueOf(10005)); - assertThat(post.getPendingBalanceDeposits()).isEmpty(); + assertThat(post.getPendingDeposits()).isEmpty(); assertThat(post.getPendingConsolidations()).isEmpty(); assertThat(post.getPendingPartialWithdrawals()).isEmpty(); } @@ -131,16 +131,19 @@ public void shouldAddNonActiveValidatorsToPendingBalanceDeposits() { final BeaconStateElectra postState = upgrade.upgrade(preState); - final SszList pendingBalanceDeposits = - postState.getPendingBalanceDeposits(); - assertThat(pendingBalanceDeposits.size()).isEqualTo(3); - assertPendingBalanceDeposit(pendingBalanceDeposits.get(0), 0, maxEffectiveBalance); + final SszList pendingDeposits = postState.getPendingDeposits(); + assertThat(pendingDeposits.size()).isEqualTo(3); + + assertPendingDeposit( + pendingDeposits.get(0), postState.getValidators().get(0), maxEffectiveBalance); // During Electra fork upgrade, pending balance deposits must be ordered by activation // eligibility epoch. // Because Validator3 (index = 2) activation eligibility epoch is lower than validator2, // Validator3's pending balance deposit must come before Validator2 (index = 1) - assertPendingBalanceDeposit(pendingBalanceDeposits.get(1), 2, maxEffectiveBalance); - assertPendingBalanceDeposit(pendingBalanceDeposits.get(2), 1, maxEffectiveBalance); + assertPendingDeposit( + pendingDeposits.get(1), postState.getValidators().get(2), maxEffectiveBalance); + assertPendingDeposit( + pendingDeposits.get(2), postState.getValidators().get(1), maxEffectiveBalance); } @Test @@ -185,20 +188,17 @@ public void shouldAddValidatorsWithCompoundingCredentialsExcessBalanceToPendingB final BeaconStateElectra postState = upgrade.upgrade(preState); - final SszList pendingBalanceDeposits = - postState.getPendingBalanceDeposits(); - assertThat(pendingBalanceDeposits.size()).isEqualTo(2); + final SszList pendingDeposits = postState.getPendingDeposits(); + assertThat(pendingDeposits.size()).isEqualTo(2); // Compounding validator will have a pending balance deposit only of the excess, in their index // order - assertPendingBalanceDeposit(pendingBalanceDeposits.get(0), 0, excessBalance); - assertPendingBalanceDeposit(pendingBalanceDeposits.get(1), 1, excessBalance); + assertPendingDeposit(pendingDeposits.get(0), postState.getValidators().get(0), excessBalance); + assertPendingDeposit(pendingDeposits.get(1), postState.getValidators().get(1), excessBalance); } - private void assertPendingBalanceDeposit( - final PendingBalanceDeposit pendingBalanceDeposit, - final int expectedValidatorIndex, - final UInt64 expectedAmount) { - assertThat(pendingBalanceDeposit.getIndex()).isEqualTo(expectedValidatorIndex); - assertThat(pendingBalanceDeposit.getAmount()).isEqualTo(expectedAmount); + private void assertPendingDeposit( + final PendingDeposit pendingDeposit, final Validator validator, final UInt64 expectedAmount) { + assertThat(pendingDeposit.getPublicKey()).isEqualTo(validator.getPublicKey()); + assertThat(pendingDeposit.getAmount()).isEqualTo(expectedAmount); } } diff --git a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectraTest.java b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectraTest.java index a586c882dc0..53d43b45603 100644 --- a/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectraTest.java +++ b/ethereum/spec/src/test/java/tech/pegasys/teku/spec/logic/versions/electra/helpers/BeaconStateMutatorsElectraTest.java @@ -29,7 +29,7 @@ import tech.pegasys.teku.spec.datastructures.state.Validator; 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.datastructures.state.versions.electra.PendingBalanceDeposit; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.schemas.SchemaDefinitionsElectra; import tech.pegasys.teku.spec.util.DataStructureUtil; @@ -68,12 +68,10 @@ public void queueExcessActiveBalance_withExcessBalance_ShouldCreatePendingBalanc final BeaconStateElectra postState = preState.updatedElectra(state -> stateMutatorsElectra.queueExcessActiveBalance(state, 0)); - final SszList postPendingBalanceDeposits = - postState.getPendingBalanceDeposits(); + final SszList postPendingDeposits = postState.getPendingDeposits(); - assertThat(postPendingBalanceDeposits.size()).isEqualTo(1); - assertThat(postPendingBalanceDeposits.get(0).getAmount()) - .isEqualTo(UInt64.valueOf(excessBalance)); + assertThat(postPendingDeposits.size()).isEqualTo(1); + assertThat(postPendingDeposits.get(0).getAmount()).isEqualTo(UInt64.valueOf(excessBalance)); } @Test @@ -87,8 +85,7 @@ public void queueExcessActiveBalance_withoutExcessBalance_ShouldNotCreatePending final BeaconStateElectra postState = preState.updatedElectra(state -> stateMutatorsElectra.queueExcessActiveBalance(state, 0)); - final SszList postPendingBalanceDeposits = - postState.getPendingBalanceDeposits(); + final SszList postPendingBalanceDeposits = postState.getPendingDeposits(); assertThat(postPendingBalanceDeposits.size()).isEqualTo(0); } @@ -107,11 +104,11 @@ public void queueExcessActiveBalance_correctlyAppendsNewBalanceDeposits() { BeaconStateElectra postState = preState.updatedElectra(state -> stateMutatorsElectra.queueExcessActiveBalance(state, 0)); - assertThat(postState.getPendingBalanceDeposits().size()).isEqualTo(1); + assertThat(postState.getPendingDeposits().size()).isEqualTo(1); postState = postState.updatedElectra(state -> stateMutatorsElectra.queueExcessActiveBalance(state, 1)); - assertThat(postState.getPendingBalanceDeposits().size()).isEqualTo(2); + assertThat(postState.getPendingDeposits().size()).isEqualTo(2); } @Test @@ -128,7 +125,7 @@ public void queueEntireBalanceAndResetValidator_updateStateAsRequired() { assertThat(preValidator.getEffectiveBalance()).isEqualTo(validatorBalance); assertThat(preValidator.getActivationEpoch()).isNotEqualTo(FAR_FUTURE_EPOCH); assertThat(preState.getBalances().get(0)).isEqualTo(SszUInt64.of(validatorBalance)); - assertThat(preState.getPendingBalanceDeposits().size()).isEqualTo(0); + assertThat(preState.getPendingDeposits().size()).isEqualTo(0); final BeaconStateElectra postState = preState.updatedElectra( @@ -144,8 +141,7 @@ public void queueEntireBalanceAndResetValidator_updateStateAsRequired() { assertThat(postState.getBalances().get(0)).isEqualTo(SszUInt64.ZERO); // Created pending balance deposit - final SszList postPendingBalanceDeposits = - postState.getPendingBalanceDeposits(); + final SszList postPendingBalanceDeposits = postState.getPendingDeposits(); assertThat(postPendingBalanceDeposits.size()).isEqualTo(1); assertThat(postPendingBalanceDeposits.get(0).getAmount()).isEqualTo(validatorBalance); } @@ -171,8 +167,9 @@ void switchToCompoundingValidator_shouldGeneratePendingBalanceDeposit() { validator.withWithdrawalCredentials( Bytes32.wrap(dataStructureUtil.randomEth1WithdrawalCredentials()))); stateMutatorsElectra.switchToCompoundingValidator(state, index); + assertThat(state.getBalances().get(index).get()).isEqualTo(UInt64.valueOf(32_000_000_000L)); - assertThat(state.getPendingBalanceDeposits().get(0).getAmount()) + assertThat(state.getPendingDeposits().get(0).getAmount()) .isEqualTo(UInt64.valueOf(1_000_000_000L)); } } diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/propertytest/suppliers/state/PendingBalanceDepositSupplier.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/propertytest/suppliers/state/PendingDepositSupplier.java similarity index 77% rename from ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/propertytest/suppliers/state/PendingBalanceDepositSupplier.java rename to ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/propertytest/suppliers/state/PendingDepositSupplier.java index c414c62d15e..04647731dae 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/propertytest/suppliers/state/PendingBalanceDepositSupplier.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/propertytest/suppliers/state/PendingDepositSupplier.java @@ -14,13 +14,12 @@ package tech.pegasys.teku.spec.propertytest.suppliers.state; import tech.pegasys.teku.spec.SpecMilestone; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.propertytest.suppliers.DataStructureUtilSupplier; import tech.pegasys.teku.spec.util.DataStructureUtil; -public class PendingBalanceDepositSupplier - extends DataStructureUtilSupplier { - public PendingBalanceDepositSupplier() { - super(DataStructureUtil::randomPendingBalanceDeposit, SpecMilestone.ELECTRA); +public class PendingDepositSupplier extends DataStructureUtilSupplier { + public PendingDepositSupplier() { + super(DataStructureUtil::randomPendingDeposit, SpecMilestone.ELECTRA); } } diff --git a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/BeaconStateBuilderElectra.java b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/BeaconStateBuilderElectra.java index b9702fda3df..794a1914e6a 100644 --- a/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/BeaconStateBuilderElectra.java +++ b/ethereum/spec/src/testFixtures/java/tech/pegasys/teku/spec/util/BeaconStateBuilderElectra.java @@ -29,8 +29,8 @@ import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateElectra; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.BeaconStateSchemaElectra; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.electra.MutableBeaconStateElectra; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; public class BeaconStateBuilderElectra @@ -55,7 +55,7 @@ public class BeaconStateBuilderElectra private UInt64 earliestConsolidationEpoch; - private SszList pendingBalanceDeposits; + private SszList pendingDeposits; private SszList pendingPartialWithdrawals; private SszList pendingConsolidations; @@ -88,7 +88,7 @@ protected void setUniqueFields(final MutableBeaconStateElectra state) { state.setEarliestExitEpoch(earliestExitEpoch); state.setConsolidationBalanceToConsume(consolidationBalanceToConsume); state.setEarliestConsolidationEpoch(earliestConsolidationEpoch); - state.setPendingBalanceDeposits(pendingBalanceDeposits); + state.setPendingDeposits(pendingDeposits); state.setPendingPartialWithdrawals(pendingPartialWithdrawals); state.setPendingConsolidations(pendingConsolidations); } @@ -172,8 +172,7 @@ protected void initDefaults() { this.earliestExitEpoch = UInt64.ZERO; this.consolidationBalanceToConsume = UInt64.ZERO; this.earliestConsolidationEpoch = UInt64.ZERO; - this.pendingBalanceDeposits = - schema.getPendingBalanceDepositsSchema().createFromElements(List.of()); + this.pendingDeposits = schema.getPendingDepositsSchema().createFromElements(List.of()); this.pendingPartialWithdrawals = schema.getPendingPartialWithdrawalsSchema().createFromElements(List.of()); this.pendingConsolidations = 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 d453b02bc73..8a46648f436 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 @@ -180,8 +180,8 @@ import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.altair.BeaconStateSchemaAltair; import tech.pegasys.teku.spec.datastructures.state.beaconstate.versions.phase0.BeaconStateSchemaPhase0; import tech.pegasys.teku.spec.datastructures.state.versions.capella.HistoricalSummary; -import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingBalanceDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingConsolidation; +import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingDeposit; import tech.pegasys.teku.spec.datastructures.state.versions.electra.PendingPartialWithdrawal; import tech.pegasys.teku.spec.datastructures.type.SszKZGCommitment; import tech.pegasys.teku.spec.datastructures.type.SszKZGProof; @@ -2531,10 +2531,15 @@ public WithdrawalRequest withdrawalRequest(final Validator validator, final UInt .create(executionAddress, validator.getPublicKey(), amount); } - public PendingBalanceDeposit randomPendingBalanceDeposit() { + public PendingDeposit randomPendingDeposit() { return getElectraSchemaDefinitions(randomSlot()) - .getPendingBalanceDepositSchema() - .create(SszUInt64.of(randomUInt64()), SszUInt64.of(randomUInt64())); + .getPendingDepositSchema() + .create( + randomSszPublicKey(), + SszBytes32.of(randomEth1WithdrawalCredentials()), + SszUInt64.of(randomUInt64()), + randomSszSignature(), + SszUInt64.of(randomUInt64())); } public ConsolidationRequest randomConsolidationRequest() { diff --git a/interop-keys/import_keys.sh b/interop-keys/import_keys.sh index c759d859ffa..88f5fb40bba 100755 --- a/interop-keys/import_keys.sh +++ b/interop-keys/import_keys.sh @@ -20,9 +20,11 @@ done TEMP=`mktemp -d` function cleanup() { + echo "Cleaning up temp folder ${TEMP}." rm -rf "${TEMP}" } trap cleanup EXIT +echo "TEMP: ${TEMP}" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" @@ -67,12 +69,20 @@ done echo "] }" >> "${TEMP}/payload.json" +# annoying but AUTHORIZATION_HEADER below seems to be a problem if its empty, so coding with and without echo "Sending payload.json to ${SIGNER_URL}/eth/v1/keystores..." - -curl --fail -q -X POST ${SIGNER_URL}/eth/v1/keystores \ - "${AUTHORIZATION_HEADER:-}" \ - -H "Content-Type: application/json" \ - -d "@${TEMP}/payload.json" \ - -o "${TEMP}/result.json" +if [ ! -z "${AUTHORIZATION_HEADER:-}" ] +then + curl --fail -q -X POST ${SIGNER_URL}/eth/v1/keystores \ + "${AUTHORIZATION_HEADER:-}" \ + -H "Content-Type: application/json" \ + -d "@${TEMP}/payload.json" \ + -o "${TEMP}/result.json" +else + curl --fail -q -X POST ${SIGNER_URL}/eth/v1/keystores \ + -H "Content-Type: application/json" \ + -d "@${TEMP}/payload.json" \ + -o "${TEMP}/result.json" +fi echo "Wrote result to ${TEMP}/result.json." diff --git a/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/AggregationSlotWrapper.java b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/AggregationSlotWrapper.java new file mode 100644 index 00000000000..a5aa76aeb54 --- /dev/null +++ b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/AggregationSlotWrapper.java @@ -0,0 +1,42 @@ +/* + * 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.validator.api.signer; + +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.UINT64_TYPE; + +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public record AggregationSlotWrapper(UInt64 slot) { + public static DeserializableTypeDefinition getJsonTypeDefinition() { + return DeserializableTypeDefinition.object(AggregationSlotWrapper.class, Builder.class) + .initializer(Builder::new) + .finisher(Builder::build) + .withField("slot", UINT64_TYPE, AggregationSlotWrapper::slot, Builder::slot) + .build(); + } + + static class Builder { + private UInt64 slot; + + Builder slot(final UInt64 slot) { + this.slot = slot; + return this; + } + + AggregationSlotWrapper build() { + return new AggregationSlotWrapper(slot); + } + } +} diff --git a/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/BlockWrapper.java b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/BlockWrapper.java new file mode 100644 index 00000000000..6c5e7db082e --- /dev/null +++ b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/BlockWrapper.java @@ -0,0 +1,47 @@ +/* + * 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.validator.api.signer; + +import static tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition.enumOf; + +import java.util.Optional; +import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; +import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlockHeader; + +public record BlockWrapper( + SpecMilestone milestone, Optional block, Optional blockHeader) { + public SerializableTypeDefinition getJsonTypeDefinition() { + return SerializableTypeDefinition.object(BlockWrapper.class) + .withField("version", enumOf(SpecMilestone.class), BlockWrapper::milestone) + .withOptionalField(SignType.BLOCK.getName(), getMaybeBlockSchema(), BlockWrapper::block) + .withOptionalField("block_header", getMaybeBlockHeaderSchema(), BlockWrapper::blockHeader) + .build(); + } + + private SerializableTypeDefinition getMaybeBlockSchema() { + return block + .>map( + beaconBlock -> beaconBlock.getSchema().getJsonTypeDefinition()) + .orElse(null); + } + + private SerializableTypeDefinition getMaybeBlockHeaderSchema() { + return blockHeader + .>map( + beaconBlockHeader -> beaconBlockHeader.getSchema().getJsonTypeDefinition()) + .orElse(null); + } +} diff --git a/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/RandaoRevealWrapper.java b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/RandaoRevealWrapper.java new file mode 100644 index 00000000000..c50cb21a82e --- /dev/null +++ b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/RandaoRevealWrapper.java @@ -0,0 +1,43 @@ +/* + * 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.validator.api.signer; + +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.UINT64_TYPE; + +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public record RandaoRevealWrapper(UInt64 epoch) { + + public static DeserializableTypeDefinition getJsonTypeDefinition() { + return DeserializableTypeDefinition.object(RandaoRevealWrapper.class, Builder.class) + .initializer(Builder::new) + .finisher(Builder::build) + .withField("epoch", UINT64_TYPE, RandaoRevealWrapper::epoch, Builder::epoch) + .build(); + } + + static class Builder { + private UInt64 epoch; + + public Builder epoch(final UInt64 epoch) { + this.epoch = epoch; + return this; + } + + public RandaoRevealWrapper build() { + return new RandaoRevealWrapper(epoch); + } + } +} diff --git a/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SignType.java b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SignType.java new file mode 100644 index 00000000000..0fd6843c1b5 --- /dev/null +++ b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SignType.java @@ -0,0 +1,42 @@ +/* + * 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.validator.api.signer; + +public enum SignType { + RANDAO_REVEAL("randao_reveal"), + BLOCK("block"), + BLOCK_V2("block_v2"), + ATTESTATION("attestation"), + AGGREGATION_SLOT("aggregation_slot"), + AGGREGATE_AND_PROOF("aggregate_and_proof"), + VOLUNTARY_EXIT("voluntary_exit"), + SYNC_COMMITTEE_MESSAGE("sync_committee_message"), + SYNC_AGGREGATOR_SELECTION_DATA("sync_aggregator_selection_data"), + SYNC_COMMITTEE_SELECTION_PROOF("sync_committee_selection_proof"), + SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF("sync_committee_contribution_and_proof"), + VALIDATOR_REGISTRATION("validator_registration"), + CONTRIBUTION_AND_PROOF("contribution_and_proof"), + BEACON_BLOCK("beacon_block"), + BLOB_SIDECAR("blob_sidecar"); + + private final String name; + + SignType(final String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SyncAggregatorSelectionDataWrapper.java b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SyncAggregatorSelectionDataWrapper.java new file mode 100644 index 00000000000..33a26dc6c4a --- /dev/null +++ b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SyncAggregatorSelectionDataWrapper.java @@ -0,0 +1,55 @@ +/* + * 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.validator.api.signer; + +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.UINT64_TYPE; + +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public record SyncAggregatorSelectionDataWrapper(UInt64 slot, UInt64 subcommitteeIndex) { + public static DeserializableTypeDefinition + getJsonTypefinition() { + return DeserializableTypeDefinition.object( + SyncAggregatorSelectionDataWrapper.class, Builder.class) + .initializer(Builder::new) + .finisher(Builder::build) + .withField("slot", UINT64_TYPE, SyncAggregatorSelectionDataWrapper::slot, Builder::slot) + .withField( + "subcommittee_index", + UINT64_TYPE, + SyncAggregatorSelectionDataWrapper::subcommitteeIndex, + Builder::subcommitteeIndex) + .build(); + } + + static class Builder { + private UInt64 slot; + private UInt64 subcommitteeIndex; + + Builder slot(final UInt64 slot) { + this.slot = slot; + return this; + } + + Builder subcommitteeIndex(final UInt64 subcommitteeIndex) { + this.subcommitteeIndex = subcommitteeIndex; + return this; + } + + SyncAggregatorSelectionDataWrapper build() { + return new SyncAggregatorSelectionDataWrapper(slot, subcommitteeIndex); + } + } +} diff --git a/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SyncCommitteeMessageWrapper.java b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SyncCommitteeMessageWrapper.java new file mode 100644 index 00000000000..038f39e99c0 --- /dev/null +++ b/validator/api/src/main/java/tech/pegasys/teku/validator/api/signer/SyncCommitteeMessageWrapper.java @@ -0,0 +1,56 @@ +/* + * 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.validator.api.signer; + +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.BYTES32_TYPE; +import static tech.pegasys.teku.infrastructure.json.types.CoreTypes.UINT64_TYPE; + +import org.apache.tuweni.bytes.Bytes32; +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; + +public record SyncCommitteeMessageWrapper(Bytes32 blockRoot, UInt64 slot) { + + public static DeserializableTypeDefinition getJsonTypeDefinition() { + return DeserializableTypeDefinition.object(SyncCommitteeMessageWrapper.class, Builder.class) + .initializer(Builder::new) + .finisher(Builder::build) + .withField("slot", UINT64_TYPE, SyncCommitteeMessageWrapper::slot, Builder::slot) + .withField( + "beacon_block_root", + BYTES32_TYPE, + SyncCommitteeMessageWrapper::blockRoot, + Builder::blockRoot) + .build(); + } + + static class Builder { + private Bytes32 blockRoot; + private UInt64 slot; + + Builder blockRoot(final Bytes32 blockRoot) { + this.blockRoot = blockRoot; + return this; + } + + Builder slot(final UInt64 slot) { + this.slot = slot; + return this; + } + + SyncCommitteeMessageWrapper build() { + return new SyncCommitteeMessageWrapper(blockRoot, slot); + } + } +} diff --git a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerAltairIntegrationTest.java b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerAltairIntegrationTest.java index cfd54a9a9c3..c84aa1543ea 100644 --- a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerAltairIntegrationTest.java +++ b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerAltairIntegrationTest.java @@ -16,7 +16,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.createForkInfo; +import static tech.pegasys.teku.validator.client.signer.ExternalSigner.FORK_INFO; import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.validateMetrics; import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.verifySignRequest; @@ -36,6 +36,9 @@ import tech.pegasys.teku.spec.datastructures.operations.versions.altair.SyncAggregatorSelectionData; import tech.pegasys.teku.spec.datastructures.operations.versions.altair.SyncCommitteeContribution; import tech.pegasys.teku.spec.logic.common.util.SyncCommitteeUtil; +import tech.pegasys.teku.validator.api.signer.SignType; +import tech.pegasys.teku.validator.api.signer.SyncAggregatorSelectionDataWrapper; +import tech.pegasys.teku.validator.api.signer.SyncCommitteeMessageWrapper; public class ExternalSignerAltairIntegrationTest extends AbstractExternalSignerIntegrationTest { @@ -79,10 +82,13 @@ void shouldSignAltairBlock() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForSignBlock(block, forkInfo), externalSignerBlockRequestProvider.getSignType(), - externalSignerBlockRequestProvider.getBlockMetadata( - Map.of("fork_info", createForkInfo(forkInfo)))); + externalSignerBlockRequestProvider.getBlockMetadata(Map.of("fork_info", forkInfo))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -109,12 +115,16 @@ public void shouldSignSyncCommitteeMessage() throws Exception { expectedSigningRoot, SignType.SYNC_COMMITTEE_MESSAGE, Map.of( - "fork_info", - createForkInfo(forkInfo), + FORK_INFO, + forkInfo, "sync_committee_message", - Map.of("beacon_block_root", beaconBlockRoot, "slot", slot))); + new SyncCommitteeMessageWrapper(beaconBlockRoot, slot))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -139,16 +149,17 @@ public void shouldSignSyncCommitteeSelectionProof() throws Exception { expectedSigningRoot, SignType.SYNC_COMMITTEE_SELECTION_PROOF, Map.of( - "fork_info", - createForkInfo(forkInfo), - "sync_aggregator_selection_data", - Map.of( - "slot", - selectionData.getSlot(), - "subcommittee_index", - selectionData.getSubcommitteeIndex()))); - - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + FORK_INFO, + forkInfo, + SignType.SYNC_AGGREGATOR_SELECTION_DATA.getName(), + new SyncAggregatorSelectionDataWrapper( + selectionData.getSlot(), selectionData.getSubcommitteeIndex()))); + + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -172,14 +183,13 @@ public void shouldSignContributionAndProof() throws Exception { new SigningRequestBody( expectedSigningRoot, SignType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, - Map.of( - "fork_info", - createForkInfo(forkInfo), - "contribution_and_proof", - new tech.pegasys.teku.api.schema.altair.ContributionAndProof( - contributionAndProof))); + Map.of(FORK_INFO, forkInfo, "contribution_and_proof", contributionAndProof)); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } diff --git a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBellatrixIntegrationTest.java b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBellatrixIntegrationTest.java index 26d9171445a..b22771da705 100644 --- a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBellatrixIntegrationTest.java +++ b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBellatrixIntegrationTest.java @@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; -import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.createForkInfo; import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.validateMetrics; import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.verifySignRequest; @@ -58,10 +57,13 @@ void shouldSignBellatrixBlock() throws Exception { new SigningRequestBody( blockHeaderSigningRoot, externalSignerBlockRequestProvider.getSignType(), - externalSignerBlockRequestProvider.getBlockMetadata( - Map.of("fork_info", createForkInfo(forkInfo)))); + externalSignerBlockRequestProvider.getBlockMetadata(Map.of("fork_info", forkInfo))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } diff --git a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerIntegrationTest.java b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerIntegrationTest.java index 234c468f579..cfa36882f88 100644 --- a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerIntegrationTest.java +++ b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerIntegrationTest.java @@ -22,7 +22,6 @@ import static tech.pegasys.teku.validator.client.signer.ExternalSigner.slashableAttestationMessage; import static tech.pegasys.teku.validator.client.signer.ExternalSigner.slashableBlockMessage; import static tech.pegasys.teku.validator.client.signer.ExternalSigner.slashableGenericMessage; -import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.createForkInfo; import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.validateMetrics; import static tech.pegasys.teku.validator.client.signer.ExternalSignerTestUtil.verifySignRequest; @@ -40,6 +39,9 @@ import tech.pegasys.teku.spec.datastructures.operations.AggregateAndProof; import tech.pegasys.teku.spec.datastructures.operations.AttestationData; import tech.pegasys.teku.spec.datastructures.operations.VoluntaryExit; +import tech.pegasys.teku.validator.api.signer.AggregationSlotWrapper; +import tech.pegasys.teku.validator.api.signer.RandaoRevealWrapper; +import tech.pegasys.teku.validator.api.signer.SignType; public class ExternalSignerIntegrationTest extends AbstractExternalSignerIntegrationTest { @@ -141,10 +143,13 @@ void shouldSignsBlock() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForSignBlock(block, forkInfo), externalSignerBlockRequestProvider.getSignType(), - externalSignerBlockRequestProvider.getBlockMetadata( - Map.of("fork_info", createForkInfo(forkInfo)))); + externalSignerBlockRequestProvider.getBlockMetadata(Map.of("fork_info", forkInfo))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -166,13 +171,13 @@ void shouldSignAttestationData() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForSignAttestationData(attestationData, forkInfo), SignType.ATTESTATION, - Map.of( - "fork_info", - createForkInfo(forkInfo), - "attestation", - new tech.pegasys.teku.api.schema.AttestationData(attestationData))); + Map.of("fork_info", forkInfo, "attestation", attestationData)); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -193,8 +198,12 @@ void shouldSignRandaoReveal() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForRandaoReveal(epoch, forkInfo), SignType.RANDAO_REVEAL, - Map.of("fork_info", createForkInfo(forkInfo), "randao_reveal", Map.of("epoch", epoch))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + Map.of("fork_info", forkInfo, "randao_reveal", new RandaoRevealWrapper(epoch))); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -216,9 +225,12 @@ public void shouldSignAggregationSlot() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForSignAggregationSlot(slot, forkInfo), SignType.AGGREGATION_SLOT, - Map.of( - "fork_info", createForkInfo(forkInfo), "aggregation_slot", Map.of("slot", slot))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + Map.of("fork_info", forkInfo, "aggregation_slot", new AggregationSlotWrapper(slot))); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -240,12 +252,12 @@ public void shouldSignAggregateAndProof() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForSignAggregateAndProof(aggregateAndProof, forkInfo), SignType.AGGREGATE_AND_PROOF, - Map.of( - "fork_info", - createForkInfo(forkInfo), - "aggregate_and_proof", - new tech.pegasys.teku.api.schema.AggregateAndProof(aggregateAndProof))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + Map.of("fork_info", forkInfo, "aggregate_and_proof", aggregateAndProof)); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -264,12 +276,12 @@ public void shouldSignVoluntaryExit() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForSignVoluntaryExit(voluntaryExit, forkInfo), SignType.VOLUNTARY_EXIT, - Map.of( - "fork_info", - createForkInfo(forkInfo), - "voluntary_exit", - new tech.pegasys.teku.api.schema.VoluntaryExit(voluntaryExit))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + Map.of("fork_info", forkInfo, "voluntary_exit", voluntaryExit)); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } @@ -290,18 +302,12 @@ public void shouldSignValidatorRegistration() throws Exception { new SigningRequestBody( signingRootUtil.signingRootForValidatorRegistration(validatorRegistration), SignType.VALIDATOR_REGISTRATION, - Map.of( - "validator_registration", - Map.of( - "fee_recipient", - validatorRegistration.getFeeRecipient().toHexString(), - "gas_limit", - validatorRegistration.getGasLimit(), - "timestamp", - validatorRegistration.getTimestamp(), - "pubkey", - validatorRegistration.getPublicKey().toString()))); - verifySignRequest(client, KEYPAIR.getPublicKey().toString(), signingRequestBody); + Map.of(SignType.VALIDATOR_REGISTRATION.getName(), validatorRegistration)); + verifySignRequest( + client, + KEYPAIR.getPublicKey().toString(), + signingRequestBody, + getSpec().getGenesisSchemaDefinitions()); validateMetrics(metricsSystem, 1, 0, 0); } } diff --git a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerTestUtil.java b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerTestUtil.java index 0a0e055b1b9..80e6ecf27db 100644 --- a/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerTestUtil.java +++ b/validator/client/src/integration-test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerTestUtil.java @@ -19,40 +19,35 @@ import static org.mockserver.model.JsonBody.json; import com.fasterxml.jackson.core.JsonProcessingException; -import java.util.Map; import org.mockserver.integration.ClientAndServer; -import org.mockserver.model.MediaType; -import tech.pegasys.teku.api.schema.Fork; +import org.mockserver.model.JsonBody; +import tech.pegasys.teku.infrastructure.json.JsonUtil; import tech.pegasys.teku.infrastructure.metrics.StubCounter; import tech.pegasys.teku.infrastructure.metrics.StubMetricsSystem; import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory; -import tech.pegasys.teku.provider.JsonProvider; -import tech.pegasys.teku.spec.datastructures.state.ForkInfo; +import tech.pegasys.teku.spec.schemas.SchemaDefinitions; class ExternalSignerTestUtil { - private static final JsonProvider JSON_PROVIDER = new JsonProvider(); static void verifySignRequest( final ClientAndServer client, final String publicKey, - final SigningRequestBody signingRequestBody) + final SigningRequestBody signingRequestBody, + final SchemaDefinitions schemaDefinitions) throws JsonProcessingException { + final JsonBody body = + json( + JsonUtil.serialize( + signingRequestBody, signingRequestBody.getJsonTypeDefinition(schemaDefinitions)), + STRICT); client.verify( request() .withMethod("POST") - .withContentType(MediaType.APPLICATION_JSON) - .withBody(json(JSON_PROVIDER.objectToJSON(signingRequestBody), STRICT)) + .withBody(body) + .withHeader("Content-Type", "application/json") .withPath(ExternalSigner.EXTERNAL_SIGNER_ENDPOINT + "/" + publicKey)); } - static Map createForkInfo(final ForkInfo forkInfo) { - return Map.of( - "genesis_validators_root", - forkInfo.getGenesisValidatorsRoot(), - "fork", - new Fork(forkInfo.getFork())); - } - static void validateMetrics( final StubMetricsSystem metricsSystem, final long successCount, diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/BlockRequestBody.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/BlockRequestBody.java deleted file mode 100644 index 8063791f587..00000000000 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/BlockRequestBody.java +++ /dev/null @@ -1,61 +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.validator.client.signer; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import tech.pegasys.teku.api.schema.BeaconBlock; -import tech.pegasys.teku.api.schema.BeaconBlockHeader; -import tech.pegasys.teku.spec.SpecMilestone; - -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BlockRequestBody { - private final SpecMilestone version; - private final BeaconBlock beaconBlock; - private final BeaconBlockHeader beaconBlockHeader; - - @JsonCreator - public BlockRequestBody( - @JsonProperty("version") final SpecMilestone version, - @JsonProperty("block") final BeaconBlock beaconBlock, - @JsonProperty("block_header") final BeaconBlockHeader beaconBlockHeader) { - this.version = version; - this.beaconBlock = beaconBlock; - this.beaconBlockHeader = beaconBlockHeader; - } - - public BlockRequestBody(final SpecMilestone version, final BeaconBlock beaconBlock) { - this(version, beaconBlock, null); - } - - public BlockRequestBody(final SpecMilestone version, final BeaconBlockHeader beaconBlockHeader) { - this(version, null, beaconBlockHeader); - } - - @JsonProperty("version") - public SpecMilestone getVersion() { - return version; - } - - @JsonProperty("block") - public BeaconBlock getBeaconBlock() { - return beaconBlock; - } - - @JsonProperty("block_header") - public BeaconBlockHeader getBeaconBlockHeader() { - return beaconBlockHeader; - } -} diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSigner.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSigner.java index b0334dee62b..24ca83fb596 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSigner.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSigner.java @@ -37,14 +37,14 @@ import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.metrics.Counter; import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; -import tech.pegasys.teku.api.schema.Fork; import tech.pegasys.teku.bls.BLSPublicKey; import tech.pegasys.teku.bls.BLSSignature; import tech.pegasys.teku.infrastructure.async.SafeFuture; import tech.pegasys.teku.infrastructure.async.ThrottlingTaskQueueWithPriority; +import tech.pegasys.teku.infrastructure.bytes.Bytes4; +import tech.pegasys.teku.infrastructure.json.JsonUtil; import tech.pegasys.teku.infrastructure.metrics.TekuMetricCategory; import tech.pegasys.teku.infrastructure.unsigned.UInt64; -import tech.pegasys.teku.provider.JsonProvider; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.builder.ValidatorRegistration; @@ -55,13 +55,19 @@ import tech.pegasys.teku.spec.datastructures.operations.versions.altair.SyncAggregatorSelectionData; import tech.pegasys.teku.spec.datastructures.state.ForkInfo; import tech.pegasys.teku.spec.logic.common.util.SyncCommitteeUtil; +import tech.pegasys.teku.spec.schemas.SchemaDefinitionCache; +import tech.pegasys.teku.spec.schemas.SchemaDefinitions; import tech.pegasys.teku.spec.signatures.Signer; import tech.pegasys.teku.spec.signatures.SigningRootUtil; +import tech.pegasys.teku.validator.api.signer.AggregationSlotWrapper; +import tech.pegasys.teku.validator.api.signer.RandaoRevealWrapper; +import tech.pegasys.teku.validator.api.signer.SignType; +import tech.pegasys.teku.validator.api.signer.SyncAggregatorSelectionDataWrapper; +import tech.pegasys.teku.validator.api.signer.SyncCommitteeMessageWrapper; public class ExternalSigner implements Signer { public static final String EXTERNAL_SIGNER_ENDPOINT = "/api/v1/eth2/sign"; - private static final String FORK_INFO = "fork_info"; - private final JsonProvider jsonProvider = new JsonProvider(); + public static final String FORK_INFO = "fork_info"; private final URL signingServiceUrl; private final BLSPublicKey blsPublicKey; private final Duration timeout; @@ -69,6 +75,7 @@ public class ExternalSigner implements Signer { private final HttpClient httpClient; private final ThrottlingTaskQueueWithPriority taskQueue; private final SigningRootUtil signingRootUtil; + private final SchemaDefinitionCache schemaDefinitionCache; private final Counter successCounter; private final Counter failedCounter; @@ -99,6 +106,7 @@ public ExternalSigner( successCounter = labelledCounter.labels("success"); failedCounter = labelledCounter.labels("failed"); timeoutCounter = labelledCounter.labels("timeout"); + this.schemaDefinitionCache = new SchemaDefinitionCache(spec); } @Override @@ -109,7 +117,8 @@ public SafeFuture createRandaoReveal(final UInt64 epoch, final For return sign( signingRootUtil.signingRootForRandaoReveal(epoch, forkInfo), SignType.RANDAO_REVEAL, - Map.of("randao_reveal", Map.of("epoch", epoch), FORK_INFO, forkInfo(forkInfo)), + Map.of( + SignType.RANDAO_REVEAL.getName(), new RandaoRevealWrapper(epoch), FORK_INFO, forkInfo), slashableGenericMessage("randao reveal")); } @@ -121,7 +130,7 @@ public SafeFuture signBlock(final BeaconBlock block, final ForkInf return sign( signingRootUtil.signingRootForSignBlock(block, forkInfo), blockRequestProvider.getSignType(), - blockRequestProvider.getBlockMetadata(Map.of(FORK_INFO, forkInfo(forkInfo))), + blockRequestProvider.getBlockMetadata(Map.of(FORK_INFO, forkInfo)), slashableBlockMessage(block.getSlot())); } @@ -131,11 +140,7 @@ public SafeFuture signAttestationData( return sign( signingRootUtil.signingRootForSignAttestationData(attestationData, forkInfo), SignType.ATTESTATION, - Map.of( - "attestation", - new tech.pegasys.teku.api.schema.AttestationData(attestationData), - FORK_INFO, - forkInfo(forkInfo)), + Map.of(SignType.ATTESTATION.getName(), attestationData, FORK_INFO, forkInfo), slashableAttestationMessage(attestationData)); } @@ -158,7 +163,11 @@ public SafeFuture signAggregationSlot(final UInt64 slot, final For sign( signingRootUtil.signingRootForSignAggregationSlot(slot, forkInfo), SignType.AGGREGATION_SLOT, - Map.of("aggregation_slot", Map.of("slot", slot), FORK_INFO, forkInfo(forkInfo)), + Map.of( + SignType.AGGREGATION_SLOT.getName(), + new AggregationSlotWrapper(slot), + FORK_INFO, + forkInfo), slashableGenericMessage("aggregation slot")), true); } @@ -169,11 +178,7 @@ public SafeFuture signAggregateAndProof( return sign( signingRootUtil.signingRootForSignAggregateAndProof(aggregateAndProof, forkInfo), SignType.AGGREGATE_AND_PROOF, - Map.of( - "aggregate_and_proof", - new tech.pegasys.teku.api.schema.AggregateAndProof(aggregateAndProof), - FORK_INFO, - forkInfo(forkInfo)), + Map.of(SignType.AGGREGATE_AND_PROOF.getName(), aggregateAndProof, FORK_INFO, forkInfo), slashableGenericMessage("aggregate and proof")); } @@ -183,11 +188,7 @@ public SafeFuture signVoluntaryExit( return sign( signingRootUtil.signingRootForSignVoluntaryExit(voluntaryExit, forkInfo), SignType.VOLUNTARY_EXIT, - Map.of( - "voluntary_exit", - new tech.pegasys.teku.api.schema.VoluntaryExit(voluntaryExit), - FORK_INFO, - forkInfo(forkInfo)), + Map.of(SignType.VOLUNTARY_EXIT.getName(), voluntaryExit, FORK_INFO, forkInfo), slashableGenericMessage("voluntary exit")); } @@ -205,10 +206,10 @@ public SafeFuture signSyncCommitteeMessage( signingRoot, SignType.SYNC_COMMITTEE_MESSAGE, Map.of( - "sync_committee_message", - Map.of("beacon_block_root", beaconBlockRoot, "slot", slot), + SignType.SYNC_COMMITTEE_MESSAGE.getName(), + new SyncCommitteeMessageWrapper(beaconBlockRoot, slot), FORK_INFO, - forkInfo(forkInfo)), + forkInfo), slashableGenericMessage("sync committee message"))); } @@ -224,14 +225,11 @@ public SafeFuture signSyncCommitteeSelectionProof( signingRoot, SignType.SYNC_COMMITTEE_SELECTION_PROOF, Map.of( - "sync_aggregator_selection_data", - Map.of( - "slot", - selectionData.getSlot(), - "subcommittee_index", - selectionData.getSubcommitteeIndex()), + SignType.SYNC_AGGREGATOR_SELECTION_DATA.getName(), + new SyncAggregatorSelectionDataWrapper( + selectionData.getSlot(), selectionData.getSubcommitteeIndex()), FORK_INFO, - forkInfo(forkInfo)), + forkInfo), slashableGenericMessage("sync committee selection proof"))); } @@ -247,11 +245,10 @@ public SafeFuture signContributionAndProof( signingRoot, SignType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, Map.of( - "contribution_and_proof", - new tech.pegasys.teku.api.schema.altair.ContributionAndProof( - contributionAndProof), + SignType.CONTRIBUTION_AND_PROOF.getName(), + contributionAndProof, FORK_INFO, - forkInfo(forkInfo)), + forkInfo), slashableGenericMessage("sync committee contribution and proof"))); } @@ -263,17 +260,7 @@ public SafeFuture signValidatorRegistration( sign( signingRootUtil.signingRootForValidatorRegistration(validatorRegistration), SignType.VALIDATOR_REGISTRATION, - Map.of( - "validator_registration", - Map.of( - "fee_recipient", - validatorRegistration.getFeeRecipient().toHexString(), - "gas_limit", - validatorRegistration.getGasLimit(), - "timestamp", - validatorRegistration.getTimestamp(), - "pubkey", - validatorRegistration.getPublicKey().toString())), + Map.of(SignType.VALIDATOR_REGISTRATION.getName(), validatorRegistration), slashableGenericMessage("validator registration"))); } @@ -287,14 +274,6 @@ private SafeFuture signingRootFromSyncCommitteeUtils( return SafeFuture.of(() -> createSigningRoot.apply(spec.getSyncCommitteeUtilRequired(slot))); } - private Map forkInfo(final ForkInfo forkInfo) { - return Map.of( - "fork", - new Fork(forkInfo.getFork()), - "genesis_validators_root", - forkInfo.getGenesisValidatorsRoot()); - } - private SafeFuture sign( final Bytes signingRoot, final SignType type, @@ -326,12 +305,31 @@ private SafeFuture sign( private String createSigningRequestBody( final Bytes signingRoot, final SignType type, final Map metadata) { try { - return jsonProvider.objectToJSON(new SigningRequestBody(signingRoot, type, metadata)); + final SigningRequestBody request = new SigningRequestBody(signingRoot, type, metadata); + final SchemaDefinitions schemaDefinitions = + getSpecVersionFromForkInfo(Optional.ofNullable((ForkInfo) metadata.get(FORK_INFO))); + return JsonUtil.serialize(request, request.getJsonTypeDefinition(schemaDefinitions)); } catch (final JsonProcessingException e) { throw new ExternalSignerException("Unable to create external signing request", e); } } + private SchemaDefinitions getSpecVersionFromForkInfo(final Optional maybeForkInfo) { + final Optional maybeFork = maybeForkInfo.map(f -> f.getFork().getCurrentVersion()); + if (maybeFork.isEmpty()) { + return schemaDefinitionCache.getSchemaDefinition( + schemaDefinitionCache.getSupportedMilestones().getFirst()); + } + final Bytes4 currentFork = maybeFork.orElseThrow(); + return spec.getEnabledMilestones().stream() + .filter(f -> f.getFork().getCurrentVersion().equals(currentFork)) + .map(f -> schemaDefinitionCache.getSchemaDefinition(f.getSpecMilestone())) + .findFirst() + .orElse( + schemaDefinitionCache.getSchemaDefinition( + schemaDefinitionCache.getSupportedMilestones().getFirst())); + } + private BLSSignature getBlsSignatureResponder( final URI url, final SignType type, @@ -353,11 +351,11 @@ private BLSSignature getBlsSignatureResponder( try { final String returnedContentType = response.headers().firstValue("Content-Type").orElse(""); - final String signatureHexStr = - returnedContentType.startsWith("application/json") - ? jsonProvider.jsonToObject(response.body(), SigningResponseBody.class).getSignature() - : response.body(); - + if (returnedContentType.startsWith("application/json")) { + return JsonUtil.parse(response.body(), SigningResponseBody.getJsonTypeDefinition()) + .signature(); + } + final String signatureHexStr = response.body(); final Bytes signature = Bytes.fromHexString(signatureHexStr); return BLSSignature.fromBytesCompressed(signature); } catch (final IllegalArgumentException | JsonProcessingException e) { diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProvider.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProvider.java index f758df116ed..462a7dd1064 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProvider.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProvider.java @@ -15,15 +15,16 @@ import java.util.HashMap; import java.util.Map; -import tech.pegasys.teku.api.SchemaObjectProvider; +import java.util.Optional; import tech.pegasys.teku.spec.Spec; import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlockHeader; +import tech.pegasys.teku.validator.api.signer.BlockWrapper; +import tech.pegasys.teku.validator.api.signer.SignType; public class ExternalSignerBlockRequestProvider { private final Spec spec; - private final SchemaObjectProvider schemaObjectProvider; private final BeaconBlock block; private final BeaconBlockHeader blockHeader; @@ -33,7 +34,6 @@ public ExternalSignerBlockRequestProvider(final Spec spec, final BeaconBlock blo this.spec = spec; this.block = block; this.blockHeader = BeaconBlockHeader.fromBlock(block); - schemaObjectProvider = new SchemaObjectProvider(spec); // backward compatible with phase 0 if (spec.atSlot(block.getSlot()).getMilestone().equals(SpecMilestone.PHASE0)) { signType = SignType.BLOCK; @@ -45,24 +45,21 @@ public ExternalSignerBlockRequestProvider(final Spec spec, final BeaconBlock blo public Map getBlockMetadata(final Map additionalEntries) { final Map metadata = new HashMap<>(additionalEntries); - final tech.pegasys.teku.api.schema.BeaconBlock beaconBlock = - block.getBody().isBlinded() - ? schemaObjectProvider.getBlindedBlock(block) - : schemaObjectProvider.getBeaconBlock(block); - final tech.pegasys.teku.api.schema.BeaconBlockHeader beaconBlockHeader = - new tech.pegasys.teku.api.schema.BeaconBlockHeader(blockHeader); - final SpecMilestone milestone = spec.atSlot(block.getSlot()).getMilestone(); switch (milestone) { case PHASE0: - metadata.put("block", beaconBlock); // backward compatible with phase0 + metadata.put(SignType.BLOCK.getName(), block); // backward compatible with phase0 break; case ALTAIR: - metadata.put("beacon_block", new BlockRequestBody(milestone, beaconBlock)); + metadata.put( + SignType.BEACON_BLOCK.getName(), + new BlockWrapper(milestone, Optional.of(block), Optional.empty())); break; default: // use block header for BELLATRIX and onward milestones - metadata.put("beacon_block", new BlockRequestBody(milestone, beaconBlockHeader)); + metadata.put( + SignType.BEACON_BLOCK.getName(), + new BlockWrapper(milestone, Optional.empty(), Optional.of(blockHeader))); } return metadata; diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerException.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerException.java index 83e18f74bff..6c298bde38b 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerException.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/ExternalSignerException.java @@ -15,6 +15,7 @@ import java.net.URI; import tech.pegasys.teku.infrastructure.http.UrlSanitizer; +import tech.pegasys.teku.validator.api.signer.SignType; public class ExternalSignerException extends RuntimeException { diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SignType.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SignType.java deleted file mode 100644 index 3cfab2e178c..00000000000 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SignType.java +++ /dev/null @@ -1,43 +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.validator.client.signer; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public enum SignType { - @JsonProperty("randao_reveal") - RANDAO_REVEAL, - @JsonProperty("block") - BLOCK, - @JsonProperty("block_v2") - BLOCK_V2, - @JsonProperty("attestation") - ATTESTATION, - @JsonProperty("aggregation_slot") - AGGREGATION_SLOT, - @JsonProperty("aggregate_and_proof") - AGGREGATE_AND_PROOF, - @JsonProperty("voluntary_exit") - VOLUNTARY_EXIT, - @JsonProperty("sync_committee_message") - SYNC_COMMITTEE_MESSAGE, - @JsonProperty("sync_committee_selection_proof") - SYNC_COMMITTEE_SELECTION_PROOF, - @JsonProperty("sync_committee_contribution_and_proof") - SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, - @JsonProperty("validator_registration") - VALIDATOR_REGISTRATION, - @JsonProperty("blob_sidecar") - BLOB_SIDECAR -} diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningRequestBody.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningRequestBody.java index dd4ed9ba698..f98c071b339 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningRequestBody.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningRequestBody.java @@ -13,51 +13,145 @@ package tech.pegasys.teku.validator.client.signer; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonProperty; -import java.util.HashMap; +import static tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition.enumOf; +import static tech.pegasys.teku.validator.client.signer.ExternalSigner.FORK_INFO; + import java.util.Map; +import java.util.Optional; import org.apache.tuweni.bytes.Bytes; +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; +import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition; +import tech.pegasys.teku.infrastructure.json.types.StringValueTypeDefinition; +import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; +import tech.pegasys.teku.spec.datastructures.builder.ValidatorRegistration; +import tech.pegasys.teku.spec.datastructures.operations.AggregateAndProof; +import tech.pegasys.teku.spec.datastructures.operations.AttestationData; +import tech.pegasys.teku.spec.datastructures.operations.VoluntaryExit; +import tech.pegasys.teku.spec.datastructures.operations.versions.altair.ContributionAndProof; +import tech.pegasys.teku.spec.datastructures.state.ForkInfo; +import tech.pegasys.teku.spec.schemas.SchemaDefinitions; +import tech.pegasys.teku.validator.api.signer.AggregationSlotWrapper; +import tech.pegasys.teku.validator.api.signer.BlockWrapper; +import tech.pegasys.teku.validator.api.signer.RandaoRevealWrapper; +import tech.pegasys.teku.validator.api.signer.SignType; +import tech.pegasys.teku.validator.api.signer.SyncAggregatorSelectionDataWrapper; +import tech.pegasys.teku.validator.api.signer.SyncCommitteeMessageWrapper; + +public record SigningRequestBody(Bytes signingRoot, SignType type, Map metadata) { + private static final StringValueTypeDefinition BYTES_TYPE = + DeserializableTypeDefinition.string(Bytes.class) + .formatter(Bytes::toHexString) + .parser(Bytes::fromHexString) + .format("byte") + .build(); + + public SerializableTypeDefinition getJsonTypeDefinition( + final SchemaDefinitions schemaDefinitions) { + return SerializableTypeDefinition.object(SigningRequestBody.class) + .withField("signingRoot", BYTES_TYPE, SigningRequestBody::signingRoot) + .withField("type", enumOf(SignType.class), SigningRequestBody::type) + .withOptionalField( + SignType.VOLUNTARY_EXIT.getName(), + VoluntaryExit.SSZ_SCHEMA.getJsonTypeDefinition(), + SigningRequestBody::getVoluntaryExit) + .withOptionalField( + SignType.AGGREGATION_SLOT.getName(), + AggregationSlotWrapper.getJsonTypeDefinition(), + SigningRequestBody::getAggregationSlot) + .withOptionalField( + FORK_INFO, ForkInfo.getJsonTypeDefinition(), SigningRequestBody::getForkInfo) + .withOptionalField( + SignType.ATTESTATION.getName(), + AttestationData.SSZ_SCHEMA.getJsonTypeDefinition(), + SigningRequestBody::getAttestationData) + .withOptionalField( + SignType.SYNC_COMMITTEE_MESSAGE.getName(), + SyncCommitteeMessageWrapper.getJsonTypeDefinition(), + SigningRequestBody::getSyncCommitteeMessage) + .withOptionalField( + SignType.SYNC_AGGREGATOR_SELECTION_DATA.getName(), + SyncAggregatorSelectionDataWrapper.getJsonTypefinition(), + SigningRequestBody::getSyncAggregateSelectionData) + .withOptionalField( + SignType.BEACON_BLOCK.getName(), + getBlockWrapper().map(BlockWrapper::getJsonTypeDefinition).orElse(null), + SigningRequestBody::getBlockWrapper) + .withOptionalField( + SignType.VALIDATOR_REGISTRATION.getName(), + ValidatorRegistration.SSZ_SCHEMA.getJsonTypeDefinition(), + SigningRequestBody::getValidatorRegistration) + .withOptionalField( + SignType.CONTRIBUTION_AND_PROOF.getName(), + getContributionAndProof().map(z -> z.getSchema().getJsonTypeDefinition()).orElse(null), + SigningRequestBody::getContributionAndProof) + .withOptionalField( + SignType.AGGREGATE_AND_PROOF.getName(), + schemaDefinitions.getAggregateAndProofSchema().getJsonTypeDefinition(), + SigningRequestBody::getAggregateAndProof) + .withOptionalField( + SignType.BLOCK.getName(), + schemaDefinitions.getBeaconBlockSchema().getJsonTypeDefinition(), + SigningRequestBody::getBlock) + .withOptionalField( + SignType.RANDAO_REVEAL.getName(), + RandaoRevealWrapper.getJsonTypeDefinition(), + SigningRequestBody::getRandaoReveal) + .build(); + } -public class SigningRequestBody { - @JsonProperty("signingRoot") - private Bytes signingRoot; + private Optional getForkInfo() { + return Optional.ofNullable((ForkInfo) metadata.get(FORK_INFO)); + } - @JsonProperty("type") - private SignType type; + private Optional getVoluntaryExit() { + return Optional.ofNullable((VoluntaryExit) metadata.get(SignType.VOLUNTARY_EXIT.getName())); + } - @JsonAnySetter private final Map metadata = new HashMap<>(); + private Optional getContributionAndProof() { + return Optional.ofNullable( + (ContributionAndProof) metadata.get(SignType.CONTRIBUTION_AND_PROOF.getName())); + } + + private Optional getAttestationData() { + return Optional.ofNullable((AttestationData) metadata.get(SignType.ATTESTATION.getName())); + } + + private Optional getAggregateAndProof() { + return Optional.ofNullable( + (AggregateAndProof) metadata.get(SignType.AGGREGATE_AND_PROOF.getName())); + } - public SigningRequestBody() { - // keeps jackson happy + private Optional getBlock() { + return Optional.ofNullable((BeaconBlock) metadata.get(SignType.BLOCK.getName())); } - public SigningRequestBody( - final Bytes signingRoot, final SignType type, final Map metadata) { - this.signingRoot = signingRoot; - this.type = type; - this.metadata.putAll(metadata); + private Optional getBlockWrapper() { + return Optional.ofNullable((BlockWrapper) metadata.get(SignType.BEACON_BLOCK.getName())); } - @JsonAnyGetter - public Map getMetadata() { - return metadata; + private Optional getSyncCommitteeMessage() { + return Optional.ofNullable( + (SyncCommitteeMessageWrapper) metadata.get(SignType.SYNC_COMMITTEE_MESSAGE.getName())); } - public void setSigningRoot(final Bytes signingRoot) { - this.signingRoot = signingRoot; + private Optional getSyncAggregateSelectionData() { + return Optional.ofNullable( + (SyncAggregatorSelectionDataWrapper) + metadata.get(SignType.SYNC_AGGREGATOR_SELECTION_DATA.getName())); } - public Bytes getSigningRoot() { - return signingRoot; + private Optional getValidatorRegistration() { + return Optional.ofNullable( + (ValidatorRegistration) metadata.get(SignType.VALIDATOR_REGISTRATION.getName())); } - public SignType getType() { - return type; + private Optional getRandaoReveal() { + return Optional.ofNullable( + (RandaoRevealWrapper) metadata.get(SignType.RANDAO_REVEAL.getName())); } - public void setType(final SignType type) { - this.type = type; + private Optional getAggregationSlot() { + return Optional.ofNullable( + (AggregationSlotWrapper) metadata.get(SignType.AGGREGATION_SLOT.getName())); } } diff --git a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningResponseBody.java b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningResponseBody.java index 30aff5446a1..4c2a6e48eb1 100644 --- a/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningResponseBody.java +++ b/validator/client/src/main/java/tech/pegasys/teku/validator/client/signer/SigningResponseBody.java @@ -13,21 +13,36 @@ package tech.pegasys.teku.validator.client.signer; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -@JsonIgnoreProperties(ignoreUnknown = true) -public class SigningResponseBody { - private final String signature; - - @JsonCreator - public SigningResponseBody( - @JsonProperty(value = "signature", required = true) final String signature) { - this.signature = signature; +import static tech.pegasys.teku.ethereum.json.types.EthereumTypes.SIGNATURE_TYPE; + +import tech.pegasys.teku.bls.BLSSignature; +import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition; + +public record SigningResponseBody(BLSSignature signature) { + + static DeserializableTypeDefinition getJsonTypeDefinition() { + return DeserializableTypeDefinition.object( + SigningResponseBody.class, SigningResponseBodyBuilder.class) + .initializer(SigningResponseBodyBuilder::new) + .finisher(SigningResponseBodyBuilder::build) + .withField( + "signature", + SIGNATURE_TYPE, + SigningResponseBody::signature, + SigningResponseBodyBuilder::signature) + .build(); } - public String getSignature() { - return signature; + static class SigningResponseBodyBuilder { + private BLSSignature signature; + + SigningResponseBodyBuilder signature(final BLSSignature signature) { + this.signature = signature; + return this; + } + + SigningResponseBody build() { + return new SigningResponseBody(signature); + } } } diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProviderTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProviderTest.java deleted file mode 100644 index 410038f49ba..00000000000 --- a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerBlockRequestProviderTest.java +++ /dev/null @@ -1,105 +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.validator.client.signer; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.Map; -import org.junit.jupiter.api.Test; -import tech.pegasys.teku.spec.Spec; -import tech.pegasys.teku.spec.SpecMilestone; -import tech.pegasys.teku.spec.TestSpecFactory; -import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; -import tech.pegasys.teku.spec.util.DataStructureUtil; - -class ExternalSignerBlockRequestProviderTest { - - @Test - void phase0BlockGeneratesCorrectSignTypeAndMetadata() { - final Spec spec = TestSpecFactory.createMinimalPhase0(); - final BeaconBlock block = new DataStructureUtil(spec).randomBeaconBlock(10); - - final ExternalSignerBlockRequestProvider externalSignerBlockRequestProvider = - new ExternalSignerBlockRequestProvider(spec, block); - final SignType signType = externalSignerBlockRequestProvider.getSignType(); - final Map blockMetadata = - externalSignerBlockRequestProvider.getBlockMetadata(Map.of()); - - assertThat(signType).isEqualTo(SignType.BLOCK); - assertThat(blockMetadata).containsKey("block"); - } - - @Test - void altairBlockGeneratesCorrectSignTypeAndMetadata() { - final Spec spec = TestSpecFactory.createMinimalAltair(); - final BeaconBlock block = new DataStructureUtil(spec).randomBeaconBlock(10); - - final ExternalSignerBlockRequestProvider externalSignerBlockRequestProvider = - new ExternalSignerBlockRequestProvider(spec, block); - final SignType signType = externalSignerBlockRequestProvider.getSignType(); - final Map blockMetadata = - externalSignerBlockRequestProvider.getBlockMetadata(Map.of()); - - assertThat(signType).isEqualTo(SignType.BLOCK_V2); - assertThat(blockMetadata).containsKey("beacon_block"); - assertThat(blockMetadata.get("beacon_block")).isExactlyInstanceOf(BlockRequestBody.class); - - final BlockRequestBody blockRequestBody = (BlockRequestBody) blockMetadata.get("beacon_block"); - assertThat(blockRequestBody.getVersion()).isEqualTo(SpecMilestone.ALTAIR); - assertThat(blockRequestBody.getBeaconBlock()).isNotNull(); - assertThat(blockRequestBody.getBeaconBlockHeader()).isNull(); - } - - @Test - void bellatrixBlockGeneratesCorrectSignTypeAndMetadata() { - final Spec spec = TestSpecFactory.createMinimalBellatrix(); - final BeaconBlock block = new DataStructureUtil(spec).randomBeaconBlock(10); - - final ExternalSignerBlockRequestProvider externalSignerBlockRequestProvider = - new ExternalSignerBlockRequestProvider(spec, block); - final SignType signType = externalSignerBlockRequestProvider.getSignType(); - final Map blockMetadata = - externalSignerBlockRequestProvider.getBlockMetadata(Map.of()); - - assertThat(signType).isEqualTo(SignType.BLOCK_V2); - assertThat(blockMetadata).containsKey("beacon_block"); - assertThat(blockMetadata.get("beacon_block")).isExactlyInstanceOf(BlockRequestBody.class); - - final BlockRequestBody blockRequestBody = (BlockRequestBody) blockMetadata.get("beacon_block"); - assertThat(blockRequestBody.getVersion()).isEqualTo(SpecMilestone.BELLATRIX); - assertThat(blockRequestBody.getBeaconBlock()).isNull(); - assertThat(blockRequestBody.getBeaconBlockHeader()).isNotNull(); - } - - @Test - void bellatrixBlindedBlockGeneratesCorrectSignTypeAndMetadata() { - final Spec spec = TestSpecFactory.createMinimalBellatrix(); - final BeaconBlock block = new DataStructureUtil(spec).randomBlindedBeaconBlock(10); - - final ExternalSignerBlockRequestProvider externalSignerBlockRequestProvider = - new ExternalSignerBlockRequestProvider(spec, block); - final SignType signType = externalSignerBlockRequestProvider.getSignType(); - final Map blockMetadata = - externalSignerBlockRequestProvider.getBlockMetadata(Map.of()); - - assertThat(signType).isEqualTo(SignType.BLOCK_V2); - assertThat(blockMetadata).containsKey("beacon_block"); - assertThat(blockMetadata.get("beacon_block")).isExactlyInstanceOf(BlockRequestBody.class); - - final BlockRequestBody blockRequestBody = (BlockRequestBody) blockMetadata.get("beacon_block"); - assertThat(blockRequestBody.getVersion()).isEqualTo(SpecMilestone.BELLATRIX); - assertThat(blockRequestBody.getBeaconBlock()).isNull(); - assertThat(blockRequestBody.getBeaconBlockHeader()).isNotNull(); - } -} diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerExceptionTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerExceptionTest.java index fe4cbc10f3e..bec549c38b3 100644 --- a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerExceptionTest.java +++ b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/ExternalSignerExceptionTest.java @@ -19,6 +19,7 @@ import java.net.URISyntaxException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import tech.pegasys.teku.validator.api.signer.SignType; class ExternalSignerExceptionTest { diff --git a/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SigningRequestBodyTest.java b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SigningRequestBodyTest.java new file mode 100644 index 00000000000..2a421661188 --- /dev/null +++ b/validator/client/src/test/java/tech/pegasys/teku/validator/client/signer/SigningRequestBodyTest.java @@ -0,0 +1,238 @@ +/* + * 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.validator.client.signer; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static tech.pegasys.teku.validator.client.signer.ExternalSigner.FORK_INFO; + +import com.google.common.io.Resources; +import java.io.IOException; +import java.util.Map; +import org.junit.jupiter.api.Test; +import tech.pegasys.teku.infrastructure.json.JsonUtil; +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.teku.spec.TestSpecFactory; +import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock; +import tech.pegasys.teku.spec.datastructures.state.ForkInfo; +import tech.pegasys.teku.spec.signatures.SigningRootUtil; +import tech.pegasys.teku.spec.util.DataStructureUtil; +import tech.pegasys.teku.validator.api.signer.AggregationSlotWrapper; +import tech.pegasys.teku.validator.api.signer.SignType; +import tech.pegasys.teku.validator.api.signer.SyncAggregatorSelectionDataWrapper; +import tech.pegasys.teku.validator.api.signer.SyncCommitteeMessageWrapper; + +class SigningRequestBodyTest { + private Spec spec; + private DataStructureUtil dataStructureUtil; + + @Test + void altairVoluntaryExitExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.VOLUNTARY_EXIT, + Map.of( + SignType.VOLUNTARY_EXIT.getName(), + dataStructureUtil.randomVoluntaryExit(), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairAggregationSlotExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.AGGREGATION_SLOT, + Map.of( + SignType.AGGREGATION_SLOT.getName(), + new AggregationSlotWrapper(dataStructureUtil.randomSlot()), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairAggregateAndProofExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.AGGREGATE_AND_PROOF, + Map.of( + SignType.AGGREGATE_AND_PROOF.getName(), + dataStructureUtil.randomAggregateAndProof(), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairSyncCommitteeSelectionProofExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.SYNC_COMMITTEE_SELECTION_PROOF, + Map.of( + SignType.SYNC_COMMITTEE_SELECTION_PROOF.getName(), + new SyncAggregatorSelectionDataWrapper( + dataStructureUtil.randomSlot(), dataStructureUtil.randomUInt64(64L)), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairSyncCommitteeContributionAndProofExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, + Map.of( + SignType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF.getName(), + dataStructureUtil.randomContributionAndProof(), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairSyncCommitteeMessageExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.SYNC_COMMITTEE_MESSAGE, + Map.of( + SignType.SYNC_COMMITTEE_MESSAGE.getName(), + new SyncCommitteeMessageWrapper( + dataStructureUtil.randomBytes32(), dataStructureUtil.randomSlot()), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairAggregationExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.AGGREGATION_SLOT, + Map.of( + SignType.AGGREGATION_SLOT.getName(), + new AggregationSlotWrapper(dataStructureUtil.randomSlot()), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void altairValidatorRegistrationExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.VALIDATOR_REGISTRATION, + Map.of( + SignType.VALIDATOR_REGISTRATION.getName(), + dataStructureUtil.randomValidatorRegistration()))); + } + + @Test + void phase0AttestationExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalPhase0(); + dataStructureUtil = new DataStructureUtil(spec); + checkSigningMessageStructure( + new SigningRequestBody( + dataStructureUtil.randomBytes32(), + SignType.ATTESTATION, + Map.of( + SignType.ATTESTATION.getName(), + dataStructureUtil.randomAttestationData(), + FORK_INFO, + dataStructureUtil.randomForkInfo()))); + } + + @Test + void phase0BlockExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalPhase0(); + dataStructureUtil = new DataStructureUtil(spec); + final BeaconBlock block = dataStructureUtil.randomBeaconBlock(); + final ExternalSignerBlockRequestProvider blockRequestProvider = + new ExternalSignerBlockRequestProvider(spec, block); + final SigningRootUtil signingRootUtil = new SigningRootUtil(spec); + final ForkInfo forkInfo = dataStructureUtil.randomForkInfo(); + checkSigningMessageStructure( + new SigningRequestBody( + signingRootUtil.signingRootForSignBlock(block, forkInfo), + blockRequestProvider.getSignType(), + blockRequestProvider.getBlockMetadata(Map.of(FORK_INFO, forkInfo)))); + } + + @Test + void altairBlockExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalAltair(); + dataStructureUtil = new DataStructureUtil(spec); + final BeaconBlock block = dataStructureUtil.randomBeaconBlock(); + final ExternalSignerBlockRequestProvider blockRequestProvider = + new ExternalSignerBlockRequestProvider(spec, block); + final SigningRootUtil signingRootUtil = new SigningRootUtil(spec); + final ForkInfo forkInfo = dataStructureUtil.randomForkInfo(); + checkSigningMessageStructure( + new SigningRequestBody( + signingRootUtil.signingRootForSignBlock(block, forkInfo), + blockRequestProvider.getSignType(), + blockRequestProvider.getBlockMetadata(Map.of(FORK_INFO, forkInfo)))); + } + + @Test + void bellatrixBlockExternalSignerMessage() throws IOException { + spec = TestSpecFactory.createMinimalBellatrix(); + dataStructureUtil = new DataStructureUtil(spec); + final BeaconBlock block = dataStructureUtil.randomBeaconBlock(); + final ExternalSignerBlockRequestProvider blockRequestProvider = + new ExternalSignerBlockRequestProvider(spec, block); + final SigningRootUtil signingRootUtil = new SigningRootUtil(spec); + final ForkInfo forkInfo = dataStructureUtil.randomForkInfo(); + checkSigningMessageStructure( + new SigningRequestBody( + signingRootUtil.signingRootForSignBlock(block, forkInfo), + blockRequestProvider.getSignType(), + blockRequestProvider.getBlockMetadata(Map.of(FORK_INFO, forkInfo)))); + } + + private void checkSigningMessageStructure(final SigningRequestBody requestBody) + throws IOException { + final String expectedJsonFile = getCurrentMethod(1) + ".json"; + final String prettyBody = + JsonUtil.prettySerialize( + requestBody, requestBody.getJsonTypeDefinition(spec.getGenesisSchemaDefinitions())); + final String expected = + Resources.toString( + Resources.getResource(SigningRequestBodyTest.class, expectedJsonFile), UTF_8); + assertThat(prettyBody).isEqualToIgnoringNewLines(expected); + } + + private static String getCurrentMethod(final int skip) { + return Thread.currentThread().getStackTrace()[1 + 1 + skip].getMethodName(); + } +} diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregateAndProofExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregateAndProofExternalSignerMessage.json new file mode 100644 index 00000000000..362dedda053 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregateAndProofExternalSignerMessage.json @@ -0,0 +1,33 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "AGGREGATE_AND_PROOF", + "fork_info" : { + "fork" : { + "previous_version" : "0x6b0ac13f", + "current_version" : "0x7e2bbb3f", + "epoch" : "4597269519555858506" + }, + "genesis_validators_root" : "0x58e9c63feadbba8eb6a9aa92fd1b7e47efe4b0e7ff7a30a3c822443ed8d731b1" + }, + "aggregate_and_proof" : { + "aggregator_index" : "4669978815449698508", + "aggregate" : { + "aggregation_bits" : "0xf222308f23fd4379911b8ed32c9edd6e5dcc54a1c8c60ff9b76abe8b7424bab6dc2d6a52647f3679db31417fe81adc8e918254e1d6824ba01812bc55517124259aaf397723a131f32e26a5bd5ee07e45cef65f229a5466940408a08475d310949f1df9ce99b1794e7d5492a0ddd68979272eb245e7af8120cda3e9cd634ad160377773aa602074207f68cf77c5397236f0d96959127eb3e6410064d81d9b14937839bdd1b6ba9aafeae4a8fd00af0ed404dc9415e27351b70eca0433acfbd873d536a730f3f32e1fd87be6802b7e01741c8bbacddc2331bc7d58c70657bd5fee190d46d1d627ba23483c1d44923d0aef3acef87d1965073b85caeac55056550e01", + "data" : { + "slot" : "4665021361504678828", + "index" : "4660063907559659148", + "beacon_block_root" : "0x8200a6402ca295554fb9562193cc71d60272d63beeaf2201fdf53e846e77f919", + "source" : { + "epoch" : "542887588", + "root" : "0x5cbeb140ec0ad7cb653388caecba483cf66bd817821ed18ca1f3b7f3b9b58a04" + }, + "target" : { + "epoch" : "535577359", + "root" : "0x32a7d23faa44fc04cc23dc3b560a55ade3deb2c393e9de2e6d20bdad2416c39b" + } + }, + "signature" : "0x992e2695a49aaedcb373280f375a08adaafafff0bab131d48674136f5e452c8bb0797618eeb99bc3a4835bca2bd6aec6067807e927604548997a795d7ba982a5274f0d1d55624a8c7b66973c6e717f3c46bd2d92bc1696d3173751a6b0bf6a63" + }, + "selection_proof" : "0xae757bc04a0f7ee8e8d1668c8de3fd4ca45ca7e8f7ad7d3323350213956386cfc97094f156a7d6ab2d3ebe6a7eb7ce2c10d0d32091ee4867224ef5856bff529e9f0c2cb9c0085a28f6a47d75aae926913f681a6b21e25b093b78e3cf188bb6be" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregationExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregationExternalSignerMessage.json new file mode 100644 index 00000000000..ecfe36eb807 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregationExternalSignerMessage.json @@ -0,0 +1,15 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "AGGREGATION_SLOT", + "aggregation_slot" : { + "slot" : "24752339414" + }, + "fork_info" : { + "fork" : { + "previous_version" : "0xfd18cf40", + "current_version" : "0x103ac940", + "epoch" : "4660063907559659148" + }, + "genesis_validators_root" : "0x8200a6402ca295554fb9562193cc71d60272d63beeaf2201fdf53e846e77f919" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregationSlotExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregationSlotExternalSignerMessage.json new file mode 100644 index 00000000000..ecfe36eb807 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairAggregationSlotExternalSignerMessage.json @@ -0,0 +1,15 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "AGGREGATION_SLOT", + "aggregation_slot" : { + "slot" : "24752339414" + }, + "fork_info" : { + "fork" : { + "previous_version" : "0xfd18cf40", + "current_version" : "0x103ac940", + "epoch" : "4660063907559659148" + }, + "genesis_validators_root" : "0x8200a6402ca295554fb9562193cc71d60272d63beeaf2201fdf53e846e77f919" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairBlockExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairBlockExternalSignerMessage.json new file mode 100644 index 00000000000..2b74f5aa3c0 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairBlockExternalSignerMessage.json @@ -0,0 +1,157 @@ +{ + "signingRoot" : "0xe561fc4eb481a01e46f822a9155c77d919cf952ae7d9802cce8079e494e5e39e", + "type" : "BLOCK_V2", + "fork_info" : { + "fork" : { + "previous_version" : "0xf8eb5a3e", + "current_version" : "0xbf886c3e", + "epoch" : "4496467996093486440" + }, + "genesis_validators_root" : "0x324f493e880f6d0bfaa9e297b9d9b45986a970f94c718be767ef67174b6fc1e9" + }, + "beacon_block" : { + "version" : "ALTAIR", + "block" : { + "slot" : "4666673844721362956", + "proposer_index" : "4665021361504678828", + "parent_root" : "0xfd18cf40cc907a739be483f1ca0ee23ad65cdd3df23205eabc6d660a75d1f54e", + "state_root" : "0x103ac9406cdc59b89027eb1c9e97f607dd5fdccfa8fb2da4eaeea9d25032add9", + "body" : { + "randao_reveal" : "0x983e008a34dda42f0c8f857f66aa82212aff48250f9ac54b30ae622d0835bdb7609c9cac67e7d19be24ffe5c77d581230e25c0e72fdf4066618bb0df7e09e66562de35b1f751004ad1ec65089c19a56a8b120983c301dbbf03df5ba674712ab4", + "eth1_data" : { + "deposit_root" : "0x8200a6402ca295554fb9562193cc71d60272d63beeaf2201fdf53e846e77f919", + "deposit_count" : "4663368873993027404", + "block_hash" : "0x5cbeb140ec0ad7cb653388caecba483cf66bd817821ed18ca1f3b7f3b9b58a04" + }, + "graffiti" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "proposer_slashings" : [ { + "signed_header_1" : { + "message" : { + "slot" : "4600574485989226763", + "proposer_index" : "4598922002772542634", + "parent_root" : "0xf943e43fcb615e36ec5aa6b9db6f1746d0d5b50d708f6400e39cf25495f39cfb", + "state_root" : "0x0c65de3f6bad3d7be19d0de5aff82b13d6d8b49f26588dba111e361d6f545486", + "body_root" : "0x6b0ac13f8a279ad3abec11bed1a49214f6e7af79b643595df6a38706b338e93b" + }, + "signature" : "0x860cc33a81805835339f1598b95691556b6f4fc5ee6a25bb24d70c658dc69d3d2e5cd62a22e14e7d962a4095e0d93ea41240a49151f9bb2884bdd1cdefcff246969101fe377460d78d58ea47c2f270e9cc8ce4bc4e81e43314bda61076350d4d" + }, + "signed_header_2" : { + "message" : { + "slot" : "4600574485989226763", + "proposer_index" : "4598922002772542634", + "parent_root" : "0x45c8cc3f4a90db49c16643672a93697ae9e1b15549b207e99aa10076fe767a26", + "state_root" : "0x58e9c63feadbba8eb6a9aa92fd1b7e47efe4b0e7ff7a30a3c822443ed8d731b1", + "body_root" : "0xb88ea93f0a5617e780f8ae6b1fc8e4480ff4abc18f66fc45ada895271cbcc666" + }, + "signature" : "0xb8f4f7eb7f1ff3eb3923e6bf36b3a0865c80f47fb8e5dbe8830751f66bd8a06a3a1e06b7b2dec66556b532721018ce940c982953c8c6176125c7dd2ba1e8cb944e10e4a14905f7135a477810872518cbac085dfc69c1759d64dab5e225a5f16c" + } + } ], + "attester_slashings" : [ { + "attestation_1" : { + "attesting_indices" : [ "4590659586689121994", "4589007099177470570", "4580744678799082634" ], + "data" : { + "slot" : "4579092195582398506", + "index" : "4584049649527418186", + "beacon_block_root" : "0xf1f1973fea38b5b560c1e4ed9a6222b021fda877b2c07674362c6080acdeec06", + "source" : { + "epoch" : "538655350", + "root" : "0x00963040ab8a07b778f467851c7d0cdc7faec2a32d5e528c900d85297e084df0" + }, + "target" : { + "epoch" : "539040099", + "root" : "0xda533c406bf3482d8e6e992e756be34172a8c47fc1cc0018350bfe98c946deda" + } + }, + "signature" : "0x8bfc6e1a1c76bdafb4d491ce02a35effde6d7362eb32c03f119c47c12fb2b49e7656bbd4702ba02560fd7fe117f2c74e02142ce46176ebf269d5b34a48a65525e35db6cc446965e86e22e9d8adf5abe92315690b6de5f4591769487539fed52a" + }, + "attestation_2" : { + "attesting_indices" : [ "4590659586689121994", "4589007099177470570", "4580744678799082634" ], + "data" : { + "slot" : "4618751809962686763", + "index" : "4623709263907706443", + "beacon_block_root" : "0x27d82440eb21c640637a36dcc38e35768bb4c0c79aefa300ec0f0cba32cabb05", + "source" : { + "epoch" : "537116355", + "root" : "0x999e0140abe701de220ca2e0b9c3b044b1c6ba33e0a3985dfe16a16b510f0846" + }, + "target" : { + "epoch" : "537501104", + "root" : "0x735c0d406b5043543786d38912b287aaa4c0bc0f731247e9a3141adb9c4d9930" + } + }, + "signature" : "0xb2213ef588828a7c18cdc781d0ed2516fd3e11de625f191aae7ae4be8b1ad2cc217728c65a500aedea276d345f09fd3212b009568a6549f5f40ead6d7ec4d0f3f329c00a1b4bca59068ee0555c94aec91bebc18365ca0b2d6692557b4b0c4267" + } + } ], + "attestations" : [ { + "aggregation_bits" : "0x46e4e5ccf2f1f586175a551fbef71fe367c10ae7f627b52680c69658efc3b9da2f03cedf49e4513374e10642a1ed264963dc671c03d571d45f02ec63fba7c7cb5897c09c76e4c5236f21c008302634d96493aae59b7960506c097817d713613d4b3e735f49272d0db6c25864be1413035ae25485ce5cb268bd0ec7a76871cc5cc0b3a892d31613f8f58fbcc1c4848be416ec389ac6b0c1e25ee635a1da4e2aeb872b99856e51958cbf99b64db166c7de9cc849fc9ca7e56dda4a7b586db617c6c55fcd252d40b15b9fbdd843a50d56cbd65f7ecffef1b632b74275254a3ee3487a305314041fa888fb642ab9d17e38e5eb338144f383c4ef9cd53141a6c9153001", + "data" : { + "slot" : "4610489389584298827", + "index" : "4608836906367614699", + "beacon_block_root" : "0x82a81c3f096d065c7e3f5d7df79bd182a53c9471a737cfb9f7c4e9ed95d0f767", + "source" : { + "epoch" : "529229002", + "root" : "0x5c66283fc9d547d293b98e264f8aa8e89836964d3ba67d459cc2625de10e8952" + }, + "target" : { + "epoch" : "529613751", + "root" : "0xcf2c053f899b836f534bfa2a45bf23b7be4890b9815a72a2aec9f70eff53d592" + } + }, + "signature" : "0x90b1100958899f808951acd4cc1d72be010f4b43fdf69587719b141b72d8410d144cac042cec8515ea74c9cbe5150c7e10b02be9ddd07421143b08f67c911f57c4a1544bc7f6a984df017e189f72aff167227b4238b50340311483aa9c843d43" + }, { + "aggregation_bits" : "0x16ffdcef1985b63b0b86daf194945f6e5c79ff31d7f9e08dee15b8fc65e74f6026349b4d5581aed81b7b945f0311e8318ae875d01c887e25386494a4151a7674dc0d279e3a2e2cb766fab0b30149410e81047bf6716e4627426e31ffa8ee22ca74c2a8db72cc801a5e912011916389fe12b8a5aee2441475ce7eb8efed2750b3d42fa1c1160c759ef11a6c999dd4bbc975a5b74a5f235cb575ef86c17418c6573d2699ff130df86057d200f042949e6997925f96fd595654ade0dec66e5b0834fdfd10f4860899ee48b80944cc981f4558aad23743e7b51e9ba22d445a5dc3601569e23e0c2c50ee21f954b0d903c7f8455cc4c66334ffec5d2095a4177766bc01", + "data" : { + "slot" : "4542737547635478505", + "index" : "4534475127257090569", + "beacon_block_root" : "0x2ed2e73ea915e0c71d9afe03676b8ab8dd578b9311463e45934f49f843386a48", + "source" : { + "epoch" : "528267130", + "root" : "0x0890f33e697e213e331430adc059611ed0518d6fa4b4ecd0384dc2678e76fb32" + }, + "target" : { + "epoch" : "527112883", + "root" : "0x7a56d03e29445ddbf2a59bb1b68edcecf66387dbea68e12d4a545719acbb4773" + } + }, + "signature" : "0x8b88a54eb155233ec6d52f2e549cacd5d9bc79e05bf0915d9278a94c9a3c75e0d75167129d10e728550df65875ecef551085599499b226b88d238a71dfdd199be5de9fde058fbaf60cf7765b0e614d3bfa76c1c47281283d7bb2ff9a30247fc5" + }, { + "aggregation_bits" : "0x703ed6e365b5bf731c1f0eb420574e1c82eec772e63229764db7d26d22edd399ebcb9efabf0ffa4739ccd17c80383fbabe45ac79e3bc7343b3d58f63c2e2f60081accefcaf44c38b4166828de77250e91c3277d8659a794cc12fa329122c631c97ca0c49886e0fd2fd661d945dd464b0ef56e83e286cf1a06feaa1ee634f5ff320b2a107ead7e4831d52e9c165b43807060efe5b45a5ed8876bd2b00959c83f37ac0f1d7ecfa7db13dca97b2dc84df4e7346f45d464d7fc8e2b9fa74af320b9d50be3887bb9a4e07a46ee9a32c8d713f85768f525671e924a66020227fbf2fb6de5bcfc9596c1153fe20674dbfd85cdd63a3e73af76327fa8e537ea3019c583601", + "data" : { + "slot" : "4574134745932346122", + "index" : "4572482262715661994", + "beacon_block_root" : "0x2a55863fca1b5384408a1a7015fd5f173406a62dd51af1a2c0af2ad93b0113a7", + "source" : { + "epoch" : "532691742", + "root" : "0x9d1b633f8ae18e21ff1b86740b32dbe55a18a0991bcfe5ffd2b6bf8a59465fe7" + }, + "target" : { + "epoch" : "531537496", + "root" : "0x77d96e3f4a4ad0971596b71d6420b24b4d12a275af3d948b77b438faa484f0d1" + } + }, + "signature" : "0x8c2c3b368bc00b3853e6df352e816dd910016db953ac77cc1565e3c22f1c0b24c59f24ea9e8ca406aa95b23368d163e80baa6de3f8f7ac19ee78c976d2ae5a21c86fa1762cc959bc734379055cb7aa1de36eae00541936b8c2ee908c770d41ff" + } ], + "deposits" : [ { + "proof" : [ "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12" ], + "data" : { + "pubkey" : "0x8394a37cbc9eeea84b75c9f76a70ff76ace5592592024093f471cb0bcc656d1b8ff2f483bce58db6162f8e7c961c5b41", + "withdrawal_credentials" : "0xc35d573fca784dabeaa154cbb2430480661e9ebd886037742eb9461b0e08cefc", + "amount" : "32000000000", + "signature" : "0xa12ef7e492b6761e5e3db657c6dbfc9221d4407013c7bded7b2c08cc01bc410f9a56a6954002755a97a4e67f60868b751443d66a10e7773ce4d841b90e0ea1972cd25fbb79cd0d452ccf4cd8474f7632d06f70c8b9cdee109e0210ff0618d517" + } + } ], + "voluntary_exits" : [ { + "message" : { + "epoch" : "4554304938742201993", + "validator_index" : "4562567354825622634" + }, + "signature" : "0x8077d47ad0fe431af45ca6ed24efda0fa9c84364ee8af5f9e83f53b3e5934961197cb31b062dcc3d5dc793ec6de565960924b65d0535f3833ecc51567572959e2849e470be8b6a1f21e2c735552595e9765e66a599d645d33fa3746d409fa122" + } ], + "sync_aggregate" : { + "sync_committee_bits" : "0x01000000", + "sync_committee_signature" : "0xaacffaf60c8253477ecad70de8589f2ef7670d0b0dc446d4baac3b465a901d3e64bb6d2c3d8bdb58aed45ac30466261416d152d5ae242204201bf6decfddde697ae0c5d44cf31ca3d43aa18f2799461fc1ee14dbf905b1e31f242fd31c083c5a" + } + } + } + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeContributionAndProofExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeContributionAndProofExternalSignerMessage.json new file mode 100644 index 00000000000..17e2e2f764d --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeContributionAndProofExternalSignerMessage.json @@ -0,0 +1,12 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF", + "fork_info" : { + "fork" : { + "previous_version" : "0x1f86d83f", + "current_version" : "0x32a7d23f", + "epoch" : "4603879456717562315" + }, + "genesis_validators_root" : "0x0c65de3f6bad3d7be19d0de5aff82b13d6d8b49f26588dba111e361d6f545486" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeMessageExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeMessageExternalSignerMessage.json new file mode 100644 index 00000000000..f7ff84b970e --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeMessageExternalSignerMessage.json @@ -0,0 +1,16 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "SYNC_COMMITTEE_MESSAGE", + "fork_info" : { + "fork" : { + "previous_version" : "0x103ac940", + "current_version" : "0x6fdfab40", + "epoch" : "4658411424342975020" + }, + "genesis_validators_root" : "0x499db7404cbff78670f0209f1932346fef68d985cb55a8d27472742bdf54d379" + }, + "sync_committee_message" : { + "slot" : "31724849254", + "beacon_block_root" : "0x367cbd40ac7318427aadb97345a91fa2e965daf3158d7f1846f1306305f41bef" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeSelectionProofExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeSelectionProofExternalSignerMessage.json new file mode 100644 index 00000000000..734c82ea2ae --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairSyncCommitteeSelectionProofExternalSignerMessage.json @@ -0,0 +1,12 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "SYNC_COMMITTEE_SELECTION_PROOF", + "fork_info" : { + "fork" : { + "previous_version" : "0x103ac940", + "current_version" : "0x6fdfab40", + "epoch" : "4658411424342975020" + }, + "genesis_validators_root" : "0x499db7404cbff78670f0209f1932346fef68d985cb55a8d27472742bdf54d379" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairValidatorRegistrationExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairValidatorRegistrationExternalSignerMessage.json new file mode 100644 index 00000000000..fa603ff6866 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairValidatorRegistrationExternalSignerMessage.json @@ -0,0 +1,10 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "VALIDATOR_REGISTRATION", + "validator_registration" : { + "fee_recipient" : "0x367cbd40ac7318427aadb97345a91fa2e965daf3", + "gas_limit" : "4669978815449698508", + "timestamp" : "4668326327938047084", + "pubkey" : "0xb7d6cb9ce7397c33b89ec57de0de383c7c294687b8963f92cc60f59bb1de46c56623cd24c9cc1e407db92d1a79920887" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairVoluntaryExitExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairVoluntaryExitExternalSignerMessage.json new file mode 100644 index 00000000000..d5d17ca5e6d --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/altairVoluntaryExitExternalSignerMessage.json @@ -0,0 +1,16 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "VOLUNTARY_EXIT", + "voluntary_exit" : { + "epoch" : "4669978815449698508", + "validator_index" : "4665021361504678828" + }, + "fork_info" : { + "fork" : { + "previous_version" : "0x103ac940", + "current_version" : "0x6fdfab40", + "epoch" : "4658411424342975020" + }, + "genesis_validators_root" : "0x499db7404cbff78670f0209f1932346fef68d985cb55a8d27472742bdf54d379" + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/bellatrixBlockExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/bellatrixBlockExternalSignerMessage.json new file mode 100644 index 00000000000..b8b37cd76fd --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/bellatrixBlockExternalSignerMessage.json @@ -0,0 +1,22 @@ +{ + "signingRoot" : "0x97efca47543ccd4fac17f16aecdc0aa9c58493fdf116ea5deaeb5e5fc34a6369", + "type" : "BLOCK_V2", + "fork_info" : { + "fork" : { + "previous_version" : "0xc7dab83e", + "current_version" : "0x8e77ca3e", + "epoch" : "4522907744740301673" + }, + "genesis_validators_root" : "0x003ea73e885578bda77a6ee17f4c6c88227980d9e6e5fe448bdc2f93a5614b3e" + }, + "beacon_block" : { + "version" : "BELLATRIX", + "block_header" : { + "slot" : "4666673844721362956", + "proposer_index" : "4665021361504678828", + "parent_root" : "0xfd18cf40cc907a739be483f1ca0ee23ad65cdd3df23205eabc6d660a75d1f54e", + "state_root" : "0x103ac9406cdc59b89027eb1c9e97f607dd5fdccfa8fb2da4eaeea9d25032add9", + "body_root" : "0xbae795425cddcee74a9be8edb9489e071ba5b436852e28f585e844d9514a9575" + } + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/phase0AttestationExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/phase0AttestationExternalSignerMessage.json new file mode 100644 index 00000000000..545df17e560 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/phase0AttestationExternalSignerMessage.json @@ -0,0 +1,25 @@ +{ + "signingRoot" : "0x235bc3400c2839fd856a524871200bd5e362db615fc4565e1870ed9a2a936464", + "type" : "ATTESTATION", + "fork_info" : { + "fork" : { + "previous_version" : "0x1f86d83f", + "current_version" : "0x32a7d23f", + "epoch" : "4603879456717562315" + }, + "genesis_validators_root" : "0x0c65de3f6bad3d7be19d0de5aff82b13d6d8b49f26588dba111e361d6f545486" + }, + "attestation" : { + "slot" : "4665021361504678828", + "index" : "4669978815449698508", + "beacon_block_root" : "0x103ac9406cdc59b89027eb1c9e97f607dd5fdccfa8fb2da4eaeea9d25032add9", + "source" : { + "epoch" : "542502839", + "root" : "0x8200a6402ca295554fb9562193cc71d60272d63beeaf2201fdf53e846e77f919" + }, + "target" : { + "epoch" : "542887588", + "root" : "0x5cbeb140ec0ad7cb653388caecba483cf66bd817821ed18ca1f3b7f3b9b58a04" + } + } +} \ No newline at end of file diff --git a/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/phase0BlockExternalSignerMessage.json b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/phase0BlockExternalSignerMessage.json new file mode 100644 index 00000000000..056e15ab972 --- /dev/null +++ b/validator/client/src/test/resources/tech/pegasys/teku/validator/client/signer/phase0BlockExternalSignerMessage.json @@ -0,0 +1,150 @@ +{ + "signingRoot" : "0x03aa76251af6013305a16908c8603dc722aa494a1660cb69776e00b3f19081c0", + "type" : "BLOCK", + "fork_info" : { + "fork" : { + "previous_version" : "0x10e23f3f", + "current_version" : "0x23033a3f", + "epoch" : "4494815512876802312" + }, + "genesis_validators_root" : "0xf8eb5a3ea82ccf3c1be1ac153e3f77f273a07343291711b9de6b9dbebc4c9b49" + }, + "block" : { + "slot" : "4666673844721362956", + "proposer_index" : "4665021361504678828", + "parent_root" : "0xfd18cf40cc907a739be483f1ca0ee23ad65cdd3df23205eabc6d660a75d1f54e", + "state_root" : "0x103ac9406cdc59b89027eb1c9e97f607dd5fdccfa8fb2da4eaeea9d25032add9", + "body" : { + "randao_reveal" : "0x983e008a34dda42f0c8f857f66aa82212aff48250f9ac54b30ae622d0835bdb7609c9cac67e7d19be24ffe5c77d581230e25c0e72fdf4066618bb0df7e09e66562de35b1f751004ad1ec65089c19a56a8b120983c301dbbf03df5ba674712ab4", + "eth1_data" : { + "deposit_root" : "0x8200a6402ca295554fb9562193cc71d60272d63beeaf2201fdf53e846e77f919", + "deposit_count" : "4663368873993027404", + "block_hash" : "0x5cbeb140ec0ad7cb653388caecba483cf66bd817821ed18ca1f3b7f3b9b58a04" + }, + "graffiti" : "0x0000000000000000000000000000000000000000000000000000000000000000", + "proposer_slashings" : [ { + "signed_header_1" : { + "message" : { + "slot" : "4600574485989226763", + "proposer_index" : "4598922002772542634", + "parent_root" : "0xf943e43fcb615e36ec5aa6b9db6f1746d0d5b50d708f6400e39cf25495f39cfb", + "state_root" : "0x0c65de3f6bad3d7be19d0de5aff82b13d6d8b49f26588dba111e361d6f545486", + "body_root" : "0x6b0ac13f8a279ad3abec11bed1a49214f6e7af79b643595df6a38706b338e93b" + }, + "signature" : "0x860cc33a81805835339f1598b95691556b6f4fc5ee6a25bb24d70c658dc69d3d2e5cd62a22e14e7d962a4095e0d93ea41240a49151f9bb2884bdd1cdefcff246969101fe377460d78d58ea47c2f270e9cc8ce4bc4e81e43314bda61076350d4d" + }, + "signed_header_2" : { + "message" : { + "slot" : "4600574485989226763", + "proposer_index" : "4598922002772542634", + "parent_root" : "0x45c8cc3f4a90db49c16643672a93697ae9e1b15549b207e99aa10076fe767a26", + "state_root" : "0x58e9c63feadbba8eb6a9aa92fd1b7e47efe4b0e7ff7a30a3c822443ed8d731b1", + "body_root" : "0xb88ea93f0a5617e780f8ae6b1fc8e4480ff4abc18f66fc45ada895271cbcc666" + }, + "signature" : "0xb8f4f7eb7f1ff3eb3923e6bf36b3a0865c80f47fb8e5dbe8830751f66bd8a06a3a1e06b7b2dec66556b532721018ce940c982953c8c6176125c7dd2ba1e8cb944e10e4a14905f7135a477810872518cbac085dfc69c1759d64dab5e225a5f16c" + } + } ], + "attester_slashings" : [ { + "attestation_1" : { + "attesting_indices" : [ "4590659586689121994", "4589007099177470570", "4580744678799082634" ], + "data" : { + "slot" : "4579092195582398506", + "index" : "4584049649527418186", + "beacon_block_root" : "0xf1f1973fea38b5b560c1e4ed9a6222b021fda877b2c07674362c6080acdeec06", + "source" : { + "epoch" : "538655350", + "root" : "0x00963040ab8a07b778f467851c7d0cdc7faec2a32d5e528c900d85297e084df0" + }, + "target" : { + "epoch" : "539040099", + "root" : "0xda533c406bf3482d8e6e992e756be34172a8c47fc1cc0018350bfe98c946deda" + } + }, + "signature" : "0x8bfc6e1a1c76bdafb4d491ce02a35effde6d7362eb32c03f119c47c12fb2b49e7656bbd4702ba02560fd7fe117f2c74e02142ce46176ebf269d5b34a48a65525e35db6cc446965e86e22e9d8adf5abe92315690b6de5f4591769487539fed52a" + }, + "attestation_2" : { + "attesting_indices" : [ "4590659586689121994", "4589007099177470570", "4580744678799082634" ], + "data" : { + "slot" : "4618751809962686763", + "index" : "4623709263907706443", + "beacon_block_root" : "0x27d82440eb21c640637a36dcc38e35768bb4c0c79aefa300ec0f0cba32cabb05", + "source" : { + "epoch" : "537116355", + "root" : "0x999e0140abe701de220ca2e0b9c3b044b1c6ba33e0a3985dfe16a16b510f0846" + }, + "target" : { + "epoch" : "537501104", + "root" : "0x735c0d406b5043543786d38912b287aaa4c0bc0f731247e9a3141adb9c4d9930" + } + }, + "signature" : "0xb2213ef588828a7c18cdc781d0ed2516fd3e11de625f191aae7ae4be8b1ad2cc217728c65a500aedea276d345f09fd3212b009568a6549f5f40ead6d7ec4d0f3f329c00a1b4bca59068ee0555c94aec91bebc18365ca0b2d6692557b4b0c4267" + } + } ], + "attestations" : [ { + "aggregation_bits" : "0x46e4e5ccf2f1f586175a551fbef71fe367c10ae7f627b52680c69658efc3b9da2f03cedf49e4513374e10642a1ed264963dc671c03d571d45f02ec63fba7c7cb5897c09c76e4c5236f21c008302634d96493aae59b7960506c097817d713613d4b3e735f49272d0db6c25864be1413035ae25485ce5cb268bd0ec7a76871cc5cc0b3a892d31613f8f58fbcc1c4848be416ec389ac6b0c1e25ee635a1da4e2aeb872b99856e51958cbf99b64db166c7de9cc849fc9ca7e56dda4a7b586db617c6c55fcd252d40b15b9fbdd843a50d56cbd65f7ecffef1b632b74275254a3ee3487a305314041fa888fb642ab9d17e38e5eb338144f383c4ef9cd53141a6c9153001", + "data" : { + "slot" : "4610489389584298827", + "index" : "4608836906367614699", + "beacon_block_root" : "0x82a81c3f096d065c7e3f5d7df79bd182a53c9471a737cfb9f7c4e9ed95d0f767", + "source" : { + "epoch" : "529229002", + "root" : "0x5c66283fc9d547d293b98e264f8aa8e89836964d3ba67d459cc2625de10e8952" + }, + "target" : { + "epoch" : "529613751", + "root" : "0xcf2c053f899b836f534bfa2a45bf23b7be4890b9815a72a2aec9f70eff53d592" + } + }, + "signature" : "0x90b1100958899f808951acd4cc1d72be010f4b43fdf69587719b141b72d8410d144cac042cec8515ea74c9cbe5150c7e10b02be9ddd07421143b08f67c911f57c4a1544bc7f6a984df017e189f72aff167227b4238b50340311483aa9c843d43" + }, { + "aggregation_bits" : "0x16ffdcef1985b63b0b86daf194945f6e5c79ff31d7f9e08dee15b8fc65e74f6026349b4d5581aed81b7b945f0311e8318ae875d01c887e25386494a4151a7674dc0d279e3a2e2cb766fab0b30149410e81047bf6716e4627426e31ffa8ee22ca74c2a8db72cc801a5e912011916389fe12b8a5aee2441475ce7eb8efed2750b3d42fa1c1160c759ef11a6c999dd4bbc975a5b74a5f235cb575ef86c17418c6573d2699ff130df86057d200f042949e6997925f96fd595654ade0dec66e5b0834fdfd10f4860899ee48b80944cc981f4558aad23743e7b51e9ba22d445a5dc3601569e23e0c2c50ee21f954b0d903c7f8455cc4c66334ffec5d2095a4177766bc01", + "data" : { + "slot" : "4542737547635478505", + "index" : "4534475127257090569", + "beacon_block_root" : "0x2ed2e73ea915e0c71d9afe03676b8ab8dd578b9311463e45934f49f843386a48", + "source" : { + "epoch" : "528267130", + "root" : "0x0890f33e697e213e331430adc059611ed0518d6fa4b4ecd0384dc2678e76fb32" + }, + "target" : { + "epoch" : "527112883", + "root" : "0x7a56d03e29445ddbf2a59bb1b68edcecf66387dbea68e12d4a545719acbb4773" + } + }, + "signature" : "0x8b88a54eb155233ec6d52f2e549cacd5d9bc79e05bf0915d9278a94c9a3c75e0d75167129d10e728550df65875ecef551085599499b226b88d238a71dfdd199be5de9fde058fbaf60cf7765b0e614d3bfa76c1c47281283d7bb2ff9a30247fc5" + }, { + "aggregation_bits" : "0x703ed6e365b5bf731c1f0eb420574e1c82eec772e63229764db7d26d22edd399ebcb9efabf0ffa4739ccd17c80383fbabe45ac79e3bc7343b3d58f63c2e2f60081accefcaf44c38b4166828de77250e91c3277d8659a794cc12fa329122c631c97ca0c49886e0fd2fd661d945dd464b0ef56e83e286cf1a06feaa1ee634f5ff320b2a107ead7e4831d52e9c165b43807060efe5b45a5ed8876bd2b00959c83f37ac0f1d7ecfa7db13dca97b2dc84df4e7346f45d464d7fc8e2b9fa74af320b9d50be3887bb9a4e07a46ee9a32c8d713f85768f525671e924a66020227fbf2fb6de5bcfc9596c1153fe20674dbfd85cdd63a3e73af76327fa8e537ea3019c583601", + "data" : { + "slot" : "4574134745932346122", + "index" : "4572482262715661994", + "beacon_block_root" : "0x2a55863fca1b5384408a1a7015fd5f173406a62dd51af1a2c0af2ad93b0113a7", + "source" : { + "epoch" : "532691742", + "root" : "0x9d1b633f8ae18e21ff1b86740b32dbe55a18a0991bcfe5ffd2b6bf8a59465fe7" + }, + "target" : { + "epoch" : "531537496", + "root" : "0x77d96e3f4a4ad0971596b71d6420b24b4d12a275af3d948b77b438faa484f0d1" + } + }, + "signature" : "0x8c2c3b368bc00b3853e6df352e816dd910016db953ac77cc1565e3c22f1c0b24c59f24ea9e8ca406aa95b23368d163e80baa6de3f8f7ac19ee78c976d2ae5a21c86fa1762cc959bc734379055cb7aa1de36eae00541936b8c2ee908c770d41ff" + } ], + "deposits" : [ { + "proof" : [ "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12", "0xe99f4b3f0a100c35d42723225a552d1a73249ce1f5f188e889bbcdabc2c93c12" ], + "data" : { + "pubkey" : "0x8394a37cbc9eeea84b75c9f76a70ff76ace5592592024093f471cb0bcc656d1b8ff2f483bce58db6162f8e7c961c5b41", + "withdrawal_credentials" : "0xc35d573fca784dabeaa154cbb2430480661e9ebd886037742eb9461b0e08cefc", + "amount" : "32000000000", + "signature" : "0xa12ef7e492b6761e5e3db657c6dbfc9221d4407013c7bded7b2c08cc01bc410f9a56a6954002755a97a4e67f60868b751443d66a10e7773ce4d841b90e0ea1972cd25fbb79cd0d452ccf4cd8474f7632d06f70c8b9cdee109e0210ff0618d517" + } + } ], + "voluntary_exits" : [ { + "message" : { + "epoch" : "4554304938742201993", + "validator_index" : "4562567354825622634" + }, + "signature" : "0x8077d47ad0fe431af45ca6ed24efda0fa9c84364ee8af5f9e83f53b3e5934961197cb31b062dcc3d5dc793ec6de565960924b65d0535f3833ecc51567572959e2849e470be8b6a1f21e2c735552595e9765e66a599d645d33fa3746d409fa122" + } ] + } + } +} \ No newline at end of file