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/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/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