diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e5a25039..311c08f3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,13 @@ - Eth2 Signing request body: deprecating `signingRoot` in favor of `signing_root` property. `signingRoot` will be removed in a future release. ### Features Added +- Signing support for BlobSidecar and BlindedBlobSidecar in Deneb fork. - Add `--azure-response-timeout` to allow request response timeout to be configurable, the field `timeout` is also accepted in the Azure metadata file. [#888](https://github.com/Consensys/web3signer/pull/888) - Bulk load Ethereum v3 wallet files in eth1 mode. +### Bugs fixed +- Upcheck was using application/json accept headers instead text/plain accept headers + ## 23.8.1 ### Bugs fixed diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2BlockSigningRequestUtil.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2BlockSigningRequestUtil.java index c2c9d987d..8616972e9 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2BlockSigningRequestUtil.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2BlockSigningRequestUtil.java @@ -84,22 +84,12 @@ public Eth2SigningRequestBody createBlockV2Request() { } public Eth2SigningRequestBody createBlockV2Request(final BlockRequest blockRequest) { - return new Eth2SigningRequestBody( - ArtifactType.BLOCK_V2, - signingRoot, - forkInfo, - null, - blockRequest, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.BLOCK_V2) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withBlockRequest(blockRequest) + .build(); } public Eth2SigningRequestBody createLegacyBlockRequest() { @@ -108,22 +98,12 @@ public Eth2SigningRequestBody createLegacyBlockRequest() { "Only PHASE0 spec is supported to create legacy BLOCK type signing request"); } - return new Eth2SigningRequestBody( - ArtifactType.BLOCK, - signingRoot, - forkInfo, - getBeaconBlock(), - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.BLOCK) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withBlock(getBeaconBlock()) + .build(); } private BeaconBlockHeader getBeaconBlockHeader() { diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2RequestUtils.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2RequestUtils.java index 133bfe578..f2e44ca2b 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2RequestUtils.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2RequestUtils.java @@ -13,6 +13,7 @@ package tech.pegasys.web3signer.dsl.utils; import static java.util.Collections.emptyList; +import static tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.BlobSidecar.fromInternalBlobSidecar; import static tech.pegasys.web3signer.core.util.DepositSigningRootUtil.computeDomain; import tech.pegasys.teku.api.schema.AggregateAndProof; @@ -33,6 +34,7 @@ import tech.pegasys.teku.spec.SpecMilestone; import tech.pegasys.teku.spec.TestSpecFactory; import tech.pegasys.teku.spec.constants.Domain; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar; import tech.pegasys.teku.spec.datastructures.operations.versions.altair.ContributionAndProof; import tech.pegasys.teku.spec.datastructures.operations.versions.altair.SyncAggregatorSelectionData; import tech.pegasys.teku.spec.datastructures.operations.versions.altair.SyncCommitteeContribution; @@ -113,6 +115,8 @@ public static Eth2SigningRequestBody createCannedRequest(final ArtifactType arti return createSyncCommitteeContributionAndProofRequest(); case VALIDATOR_REGISTRATION: return createValidatorRegistrationRequest(); + case BLOB_SIDECAR: + return createBlobSidecarRequest(); default: throw new IllegalStateException("Unknown eth2 signing type"); } @@ -144,23 +148,12 @@ private static Eth2SigningRequestBody createAggregateAndProof() { final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForSignAggregateAndProof( aggregateAndProof.asInternalAggregateAndProof(SPEC), forkInfo.asInternalForkInfo()); - - return new Eth2SigningRequestBody( - ArtifactType.AGGREGATE_AND_PROOF, - signingRoot, - forkInfo, - null, - null, - null, - null, - aggregateAndProof, - null, - null, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.AGGREGATE_AND_PROOF) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withAggregateAndProof(aggregateAndProof) + .build(); } private static Eth2SigningRequestBody createAggregationSlot() { @@ -169,22 +162,13 @@ private static Eth2SigningRequestBody createAggregationSlot() { final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForSignAggregationSlot( aggregationSlot.getSlot(), forkInfo.asInternalForkInfo()); - return new Eth2SigningRequestBody( - ArtifactType.AGGREGATION_SLOT, - signingRoot, - forkInfo, - null, - null, - null, - aggregationSlot, - null, - null, - null, - null, - null, - null, - null, - null); + + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.AGGREGATION_SLOT) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withAggregationSlot(aggregationSlot) + .build(); } private static Eth2SigningRequestBody createAttestationRequest() { @@ -197,22 +181,12 @@ private static Eth2SigningRequestBody createRandaoReveal() { final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForRandaoReveal( randaoReveal.getEpoch(), forkInfo.asInternalForkInfo()); - return new Eth2SigningRequestBody( - ArtifactType.RANDAO_REVEAL, - signingRoot, - forkInfo, - null, - null, - null, - null, - null, - null, - randaoReveal, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.RANDAO_REVEAL) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withRandaoReveal(randaoReveal) + .build(); } private static Eth2SigningRequestBody createVoluntaryExit() { @@ -221,22 +195,12 @@ private static Eth2SigningRequestBody createVoluntaryExit() { final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForSignVoluntaryExit( voluntaryExit.asInternalVoluntaryExit(), forkInfo.asInternalForkInfo()); - return new Eth2SigningRequestBody( - ArtifactType.VOLUNTARY_EXIT, - signingRoot, - forkInfo, - null, - null, - null, - null, - null, - voluntaryExit, - null, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.VOLUNTARY_EXIT) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withVoluntaryExit(voluntaryExit) + .build(); } private static Eth2SigningRequestBody createDepositRequest() { @@ -251,22 +215,11 @@ private static Eth2SigningRequestBody createDepositRequest() { final Bytes signingRoot = DepositSigningRootUtil.computeSigningRoot( depositMessage.asInternalDepositMessage(), depositDomain); - return new Eth2SigningRequestBody( - ArtifactType.DEPOSIT, - signingRoot, - null, - null, - null, - null, - null, - null, - null, - null, - depositMessage, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.DEPOSIT) + .withSigningRoot(signingRoot) + .withDeposit(depositMessage) + .build(); } public static Eth2SigningRequestBody createAttestationRequest( @@ -286,22 +239,12 @@ public static Eth2SigningRequestBody createAttestationRequest( final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForSignAttestationData( attestationData.asInternalAttestationData(), forkInfo.asInternalForkInfo()); - return new Eth2SigningRequestBody( - ArtifactType.ATTESTATION, - signingRoot, - forkInfo, - null, - null, - attestationData, - null, - null, - null, - null, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.ATTESTATION) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withAttestation(attestationData) + .build(); } public static Eth2SigningRequestBody createBlockRequest() { @@ -338,22 +281,12 @@ public static Eth2SigningRequestBody createBlockRequest( final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForSignBlock( block.asInternalBeaconBlock(SPEC), forkInfo.asInternalForkInfo()); - return new Eth2SigningRequestBody( - ArtifactType.BLOCK, - signingRoot, - forkInfo, - block, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.BLOCK) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withBlock(block) + .build(); } public static ForkInfo forkInfo() { @@ -395,23 +328,12 @@ private static Eth2SigningRequestBody createSyncCommitteeMessageRequest() { final SyncCommitteeMessage syncCommitteeMessage = new SyncCommitteeMessage(BEACON_BLOCK_ROOT, SLOT); - - return new Eth2SigningRequestBody( - ArtifactType.SYNC_COMMITTEE_MESSAGE, - signingRoot, - forkInfo, - null, - null, - null, - null, - null, - null, - null, - null, - syncCommitteeMessage, - null, - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.SYNC_COMMITTEE_MESSAGE) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withSyncCommitteeMessage(syncCommitteeMessage) + .build(); } private static Eth2SigningRequestBody createSyncCommitteeSelectionProofRequest() { @@ -433,22 +355,12 @@ private static Eth2SigningRequestBody createSyncCommitteeSelectionProofRequest() throw new RuntimeException(e); } - return new Eth2SigningRequestBody( - ArtifactType.SYNC_COMMITTEE_SELECTION_PROOF, - signingRoot, - forkInfo, - null, - null, - null, - null, - null, - null, - null, - null, - null, - getSyncAggregatorSelectionData(SLOT, subcommitteeIndex), - null, - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.SYNC_COMMITTEE_SELECTION_PROOF) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withSyncAggregatorSelectionData(getSyncAggregatorSelectionData(SLOT, subcommitteeIndex)) + .build(); } private static tech.pegasys.web3signer.core.service.http.handlers.signing.eth2 @@ -473,22 +385,12 @@ private static Eth2SigningRequestBody createSyncCommitteeContributionAndProofReq throw new RuntimeException(e); } - return new Eth2SigningRequestBody( - ArtifactType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, - signingRoot, - forkInfo, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - getContributionAndProof(), - null); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withContributionAndProof(getContributionAndProof()) + .build(); } private static Eth2SigningRequestBody createValidatorRegistrationRequest() { @@ -501,22 +403,11 @@ private static Eth2SigningRequestBody createValidatorRegistrationRequest() { final Bytes signingRoot = SIGNING_ROOT_UTIL.signingRootForValidatorRegistration( validatorRegistration.asInternalValidatorRegistration()); - return new Eth2SigningRequestBody( - ArtifactType.VALIDATOR_REGISTRATION, - signingRoot, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - validatorRegistration); + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.VALIDATOR_REGISTRATION) + .withSigningRoot(signingRoot) + .withValidatorRegistration(validatorRegistration) + .build(); } private static tech.pegasys.teku.api.schema.altair.ContributionAndProof @@ -533,4 +424,27 @@ private static SafeFuture signingRootFromSyncCommitteeUtils( return SafeFuture.of( () -> createSigningRoot.apply(ALTAIR_SPEC.getSyncCommitteeUtilRequired(slot))); } + + public static Eth2SigningRequestBody createBlobSidecarRequest() { + final Spec spec = TestSpecFactory.createMinimal(SpecMilestone.DENEB); + final tech.pegasys.teku.spec.datastructures.state.ForkInfo tekuForkInfo = + Eth2RequestUtils.forkInfo().asInternalForkInfo(); + final Fork tekuFork = new Fork(tekuForkInfo.getFork()); + final tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.ForkInfo forkInfo = + new tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.ForkInfo( + tekuFork, tekuForkInfo.getGenesisValidatorsRoot()); + final Bytes signingRoot; + + // generate random blobsidecar + final BlobSidecar tekuBlobSidecar = new DataStructureUtil(spec).randomBlobSidecar(); + signingRoot = + new SigningRootUtil(spec).signingRootForBlobSidecar(tekuBlobSidecar, tekuForkInfo); + + return Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.BLOB_SIDECAR) + .withSigningRoot(signingRoot) + .withForkInfo(forkInfo) + .withBlobSidecar(fromInternalBlobSidecar(tekuBlobSidecar)) + .build(); + } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2SigningRequestBodyBuilder.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2SigningRequestBodyBuilder.java new file mode 100644 index 000000000..7c86c2703 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/utils/Eth2SigningRequestBodyBuilder.java @@ -0,0 +1,161 @@ +/* + * Copyright 2023 ConsenSys AG. + * + * 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.web3signer.dsl.utils; + +import tech.pegasys.teku.api.schema.AggregateAndProof; +import tech.pegasys.teku.api.schema.AttestationData; +import tech.pegasys.teku.api.schema.BeaconBlock; +import tech.pegasys.teku.api.schema.VoluntaryExit; +import tech.pegasys.teku.api.schema.altair.ContributionAndProof; +import tech.pegasys.web3signer.core.service.http.ArtifactType; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.AggregationSlot; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.BlobSidecar; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.BlockRequest; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.DepositMessage; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.Eth2SigningRequestBody; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.ForkInfo; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.RandaoReveal; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.SyncAggregatorSelectionData; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.SyncCommitteeMessage; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.ValidatorRegistration; + +import org.apache.tuweni.bytes.Bytes; + +public final class Eth2SigningRequestBodyBuilder { + private ArtifactType type; + private Bytes signingRoot; + private ForkInfo forkInfo; + private BeaconBlock block; + private BlockRequest blockRequest; + private AttestationData attestation; + private AggregationSlot aggregationSlot; + private AggregateAndProof aggregateAndProof; + private VoluntaryExit voluntaryExit; + private RandaoReveal randaoReveal; + private DepositMessage deposit; + private SyncCommitteeMessage syncCommitteeMessage; + private SyncAggregatorSelectionData syncAggregatorSelectionData; + private ContributionAndProof contributionAndProof; + private ValidatorRegistration validatorRegistration; + private BlobSidecar blobSidecar; + + private Eth2SigningRequestBodyBuilder() {} + + public static Eth2SigningRequestBodyBuilder anEth2SigningRequestBody() { + return new Eth2SigningRequestBodyBuilder(); + } + + public Eth2SigningRequestBodyBuilder withType(ArtifactType type) { + this.type = type; + return this; + } + + public Eth2SigningRequestBodyBuilder withSigningRoot(Bytes signingRoot) { + this.signingRoot = signingRoot; + return this; + } + + public Eth2SigningRequestBodyBuilder withForkInfo(ForkInfo forkInfo) { + this.forkInfo = forkInfo; + return this; + } + + public Eth2SigningRequestBodyBuilder withBlock(BeaconBlock block) { + this.block = block; + return this; + } + + public Eth2SigningRequestBodyBuilder withBlockRequest(BlockRequest blockRequest) { + this.blockRequest = blockRequest; + return this; + } + + public Eth2SigningRequestBodyBuilder withAttestation(AttestationData attestation) { + this.attestation = attestation; + return this; + } + + public Eth2SigningRequestBodyBuilder withAggregationSlot(AggregationSlot aggregationSlot) { + this.aggregationSlot = aggregationSlot; + return this; + } + + public Eth2SigningRequestBodyBuilder withAggregateAndProof(AggregateAndProof aggregateAndProof) { + this.aggregateAndProof = aggregateAndProof; + return this; + } + + public Eth2SigningRequestBodyBuilder withVoluntaryExit(VoluntaryExit voluntaryExit) { + this.voluntaryExit = voluntaryExit; + return this; + } + + public Eth2SigningRequestBodyBuilder withRandaoReveal(RandaoReveal randaoReveal) { + this.randaoReveal = randaoReveal; + return this; + } + + public Eth2SigningRequestBodyBuilder withDeposit(DepositMessage deposit) { + this.deposit = deposit; + return this; + } + + public Eth2SigningRequestBodyBuilder withSyncCommitteeMessage( + SyncCommitteeMessage syncCommitteeMessage) { + this.syncCommitteeMessage = syncCommitteeMessage; + return this; + } + + public Eth2SigningRequestBodyBuilder withSyncAggregatorSelectionData( + SyncAggregatorSelectionData syncAggregatorSelectionData) { + this.syncAggregatorSelectionData = syncAggregatorSelectionData; + return this; + } + + public Eth2SigningRequestBodyBuilder withContributionAndProof( + ContributionAndProof contributionAndProof) { + this.contributionAndProof = contributionAndProof; + return this; + } + + public Eth2SigningRequestBodyBuilder withValidatorRegistration( + ValidatorRegistration validatorRegistration) { + this.validatorRegistration = validatorRegistration; + return this; + } + + public Eth2SigningRequestBodyBuilder withBlobSidecar(BlobSidecar blobSidecar) { + this.blobSidecar = blobSidecar; + return this; + } + + public Eth2SigningRequestBody build() { + return new Eth2SigningRequestBody( + type, + signingRoot, + forkInfo, + block, + blockRequest, + attestation, + aggregationSlot, + aggregateAndProof, + voluntaryExit, + randaoReveal, + deposit, + syncCommitteeMessage, + syncAggregatorSelectionData, + contributionAndProof, + validatorRegistration, + blobSidecar); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/UpcheckAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/UpcheckAcceptanceTest.java index 6f2f090d7..e21ecc9b2 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/UpcheckAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/UpcheckAcceptanceTest.java @@ -28,6 +28,7 @@ void upcheckOnCorrectPortRespondsWithOK() { given() .baseUri(signer.getUrl()) + .accept(ContentType.TEXT) .get("/upcheck") .then() .statusCode(200) diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsBlobSidecarSigningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsBlobSidecarSigningAcceptanceTest.java new file mode 100644 index 000000000..fe0a957d3 --- /dev/null +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsBlobSidecarSigningAcceptanceTest.java @@ -0,0 +1,68 @@ +/* + * Copyright 2023 ConsenSys AG. + * + * 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.web3signer.tests.signing; + +import static io.restassured.http.ContentType.JSON; +import static org.assertj.core.api.Assertions.assertThat; + +import tech.pegasys.teku.bls.BLS; +import tech.pegasys.teku.bls.BLSKeyPair; +import tech.pegasys.teku.bls.BLSPublicKey; +import tech.pegasys.teku.bls.BLSSecretKey; +import tech.pegasys.teku.spec.SpecMilestone; +import tech.pegasys.teku.spec.networks.Eth2Network; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.Eth2SigningRequestBody; +import tech.pegasys.web3signer.dsl.utils.Eth2RequestUtils; +import tech.pegasys.web3signer.dsl.utils.MetadataFileHelpers; +import tech.pegasys.web3signer.signing.KeyType; + +import java.nio.file.Path; + +import com.fasterxml.jackson.core.JsonProcessingException; +import io.restassured.response.Response; +import org.apache.tuweni.bytes.Bytes; +import org.apache.tuweni.bytes.Bytes32; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class BlsBlobSidecarSigningAcceptanceTest extends SigningAcceptanceTestBase { + + private static final String PRIVATE_KEY = + "3ee2224386c82ffea477e2adf28a2929f5c349165a4196158c7f3a2ecca40f35"; + private static final MetadataFileHelpers METADATA_FILE_HELPERS = new MetadataFileHelpers(); + private static final BLSSecretKey KEY = + BLSSecretKey.fromBytes(Bytes32.fromHexString(PRIVATE_KEY)); + private static final BLSKeyPair KEY_PAIR = new BLSKeyPair(KEY); + private static final BLSPublicKey PUBLIC_KEY = KEY_PAIR.getPublicKey(); + + @BeforeEach + void init() { + final String configFilename = PUBLIC_KEY.toString().substring(2); + final Path keyConfigFile = testDirectory.resolve(configFilename + ".yaml"); + METADATA_FILE_HELPERS.createUnencryptedYamlFileAt(keyConfigFile, PRIVATE_KEY, KeyType.BLS); + + setupEth2Signer(Eth2Network.MINIMAL, SpecMilestone.DENEB); + } + + @Test + public void signBlobSidecar() throws JsonProcessingException { + final Eth2SigningRequestBody request = Eth2RequestUtils.createBlobSidecarRequest(); + + final Response response = signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), request, JSON); + final Bytes signature = verifyAndGetSignatureResponse(response, JSON); + + final Bytes expectedSignature = + BLS.sign(KEY_PAIR.getSecretKey(), request.signingRoot()).toBytesCompressed(); + assertThat(signature).isEqualTo(expectedSignature); + } +} diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningAcceptanceTest.java index 7a09b01da..5bcf5ad60 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningAcceptanceTest.java @@ -246,21 +246,22 @@ public void failsIfSigningRootDoesNotMatchSigningData(final ArtifactType artifac final Eth2SigningRequestBody request = Eth2RequestUtils.createCannedRequest(artifactType); final Eth2SigningRequestBody requestWithMismatchedSigningRoot = new Eth2SigningRequestBody( - request.getType(), + request.type(), Bytes32.ZERO, - request.getForkInfo(), - request.getBlock(), - request.getBlockRequest(), - request.getAttestation(), - request.getAggregationSlot(), - request.getAggregateAndProof(), - request.getVoluntaryExit(), - request.getRandaoReveal(), - request.getDeposit(), - request.getSyncCommitteeMessage(), - request.getSyncAggregatorSelectionData(), - request.getContributionAndProof(), - request.getValidatorRegistration()); + request.forkInfo(), + request.block(), + request.blockRequest(), + request.attestation(), + request.aggregationSlot(), + request.aggregateAndProof(), + request.voluntaryExit(), + request.randaoReveal(), + request.deposit(), + request.syncCommitteeMessage(), + request.syncAggregatorSelectionData(), + request.contributionAndProof(), + request.validatorRegistration(), + request.blobSidecar()); final Response response = signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), requestWithMismatchedSigningRoot); @@ -284,21 +285,22 @@ public void ableToSignWithoutSigningRootField(final ContentType acceptableConten final Eth2SigningRequestBody requestWithMismatchedSigningRoot = new Eth2SigningRequestBody( - request.getType(), + request.type(), null, - request.getForkInfo(), - request.getBlock(), - request.getBlockRequest(), - request.getAttestation(), - request.getAggregationSlot(), - request.getAggregateAndProof(), - request.getVoluntaryExit(), - request.getRandaoReveal(), - request.getDeposit(), - request.getSyncCommitteeMessage(), - request.getSyncAggregatorSelectionData(), - request.getContributionAndProof(), - request.getValidatorRegistration()); + request.forkInfo(), + request.block(), + request.blockRequest(), + request.attestation(), + request.aggregationSlot(), + request.aggregateAndProof(), + request.voluntaryExit(), + request.randaoReveal(), + request.deposit(), + request.syncCommitteeMessage(), + request.syncAggregatorSelectionData(), + request.contributionAndProof(), + request.validatorRegistration(), + request.blobSidecar()); final Response response = signer.eth2Sign( @@ -335,22 +337,16 @@ private void signAndVerifySignature( signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), request, acceptMediaType); final Bytes signature = verifyAndGetSignatureResponse(response, expectedContentType(acceptMediaType)); - final BLSSignature expectedSignature = - BLS.sign(KEY_PAIR.getSecretKey(), request.getSigningRoot()); + final BLSSignature expectedSignature = BLS.sign(KEY_PAIR.getSecretKey(), request.signingRoot()); assertThat(signature).isEqualTo(expectedSignature.toBytesCompressed()); } private void setupMinimalWeb3Signer(final ArtifactType artifactType) { switch (artifactType) { - case BLOCK_V2: - case SYNC_COMMITTEE_MESSAGE: - case SYNC_COMMITTEE_SELECTION_PROOF: - case SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF: - setupEth2Signer(Eth2Network.MINIMAL, SpecMilestone.ALTAIR); - break; - default: - setupEth2Signer(Eth2Network.MINIMAL, SpecMilestone.PHASE0); - break; + case BLOCK_V2, SYNC_COMMITTEE_MESSAGE, SYNC_COMMITTEE_SELECTION_PROOF, SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF -> setupEth2Signer( + Eth2Network.MINIMAL, SpecMilestone.ALTAIR); + case BLOB_SIDECAR -> setupEth2Signer(Eth2Network.MINIMAL, SpecMilestone.DENEB); + default -> setupEth2Signer(Eth2Network.MINIMAL, SpecMilestone.PHASE0); } } diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningRootPropertyAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningRootPropertyAcceptanceTest.java index d5ba83f51..6719f2cb1 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningRootPropertyAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/BlsSigningRootPropertyAcceptanceTest.java @@ -83,8 +83,7 @@ private void signAndVerifySignature(final String signingRootProperty) signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), modifiedJsonBody, acceptMediaType); final Bytes signature = verifyAndGetSignatureResponse(response, expectedContentType(acceptMediaType)); - final BLSSignature expectedSignature = - BLS.sign(KEY_PAIR.getSecretKey(), request.getSigningRoot()); + final BLSSignature expectedSignature = BLS.sign(KEY_PAIR.getSecretKey(), request.signingRoot()); assertThat(signature).isEqualTo(expectedSignature.toBytesCompressed()); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2BlockSigningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2BlockSigningAcceptanceTest.java index 3b524e965..ec329e56f 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2BlockSigningAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2BlockSigningAcceptanceTest.java @@ -69,8 +69,7 @@ void signAndVerifyBlockV2Signature(final SpecMilestone specMilestone) throws Exc final Response response = signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), request, ContentType.JSON); final Bytes signature = verifyAndGetSignatureResponse(response, ContentType.JSON); - final BLSSignature expectedSignature = - BLS.sign(KEY_PAIR.getSecretKey(), request.getSigningRoot()); + final BLSSignature expectedSignature = BLS.sign(KEY_PAIR.getSecretKey(), request.signingRoot()); assertThat(signature).isEqualTo(expectedSignature.toBytesCompressed()); } @@ -83,8 +82,7 @@ void signAndVerifyLegacyBlockSignature() throws Exception { final Response response = signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), request, ContentType.JSON); final Bytes signature = verifyAndGetSignatureResponse(response, ContentType.JSON); - final BLSSignature expectedSignature = - BLS.sign(KEY_PAIR.getSecretKey(), request.getSigningRoot()); + final BLSSignature expectedSignature = BLS.sign(KEY_PAIR.getSecretKey(), request.signingRoot()); assertThat(signature).isEqualTo(expectedSignature.toBytesCompressed()); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2CustomNetworkFileAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2CustomNetworkFileAcceptanceTest.java index 53829369e..5862c24bb 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2CustomNetworkFileAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2CustomNetworkFileAcceptanceTest.java @@ -89,7 +89,7 @@ void signAndVerifyBlockV2SignatureForAllEnabledMilestones() throws Exception { createBlockV2SigningRequest(spec, forkAndSpecMilestone); final Bytes signingRootSignature = sendSignRequestAndReceiveSignature(request); final Bytes expectedSigningRootSignature = - calculateSigningRootSignature(request.getSigningRoot()); + calculateSigningRootSignature(request.signingRoot()); assertThat(signingRootSignature).isEqualTo(expectedSigningRootSignature); } diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2DepositSigningAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2DepositSigningAcceptanceTest.java index 6d583e90c..2f9ee969a 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2DepositSigningAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/Eth2DepositSigningAcceptanceTest.java @@ -24,6 +24,7 @@ import tech.pegasys.web3signer.core.service.http.ArtifactType; import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.DepositMessage; import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.Eth2SigningRequestBody; +import tech.pegasys.web3signer.dsl.utils.Eth2SigningRequestBodyBuilder; import tech.pegasys.web3signer.dsl.utils.MetadataFileHelpers; import java.io.IOException; @@ -102,22 +103,10 @@ private void verifyDepositData(final Map depositData) throws IOE UInt64.valueOf(depositData.get("amount")), Bytes4.fromHexString(depositData.get("fork_version"))); final Eth2SigningRequestBody requestBody = - new Eth2SigningRequestBody( - ArtifactType.DEPOSIT, - null, - null, - null, - null, - null, - null, - null, - null, - null, - depositMessage, - null, - null, - null, - null); + Eth2SigningRequestBodyBuilder.anEth2SigningRequestBody() + .withType(ArtifactType.DEPOSIT) + .withDeposit(depositMessage) + .build(); final Response response = signer.eth2Sign(publicKey, requestBody, TEXT); diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/KeyLoadAndSignAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/KeyLoadAndSignAcceptanceTest.java index 6d9167440..86ed443fd 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/KeyLoadAndSignAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/signing/KeyLoadAndSignAcceptanceTest.java @@ -154,7 +154,7 @@ public void unusedFieldsInRequestDoesNotAffectSigning() throws JsonProcessingExc new JsonObject(ETH_2_INTERFACE_OBJECT_MAPPER.writeValueAsString(blockRequest)); final String body = jsonObject.put("unknownField", "someValue").toString(); final String expectedSignature = - BLS.sign(BLS_KEY_PAIR.getSecretKey(), blockRequest.getSigningRoot()).toString(); + BLS.sign(BLS_KEY_PAIR.getSecretKey(), blockRequest.signingRoot()).toString(); given() .baseUri(signer.getUrl()) diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/slashing/SlashingExportAcceptanceTest.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/slashing/SlashingExportAcceptanceTest.java index 35c3f3f6e..a50443374 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/slashing/SlashingExportAcceptanceTest.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/tests/slashing/SlashingExportAcceptanceTest.java @@ -113,9 +113,9 @@ void slashingDataIsExported(@TempDir final Path testDirectory) throws IOExceptio assertThat(artifacts.getSignedAttestations()).hasSize(1); final SignedAttestation attestation = artifacts.getSignedAttestations().get(0); assertThat(attestation.getSourceEpoch().toLong()) - .isEqualTo(request.getAttestation().source.epoch.longValue()); + .isEqualTo(request.attestation().source.epoch.longValue()); assertThat(attestation.getTargetEpoch().toLong()) - .isEqualTo(request.getAttestation().target.epoch.longValue()); + .isEqualTo(request.attestation().target.epoch.longValue()); assertThat(attestation.getSigningRoot()).isNotNull(); } } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/Runner.java b/core/src/main/java/tech/pegasys/web3signer/core/Runner.java index 56cb4fc86..e4e0716b2 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/Runner.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/Runner.java @@ -77,6 +77,7 @@ public abstract class Runner implements Runnable, AutoCloseable { public static final String JSON = HttpHeaderValues.APPLICATION_JSON.toString(); + public static final String TEXT_PLAIN = HttpHeaderValues.TEXT_PLAIN.toString(); public static final String HEALTHCHECK_PATH = "/healthcheck"; public static final String UPCHECK_PATH = "/upcheck"; public static final String RELOAD_PATH = "/reload"; @@ -253,7 +254,7 @@ protected void addReloadHandler( private void registerUpcheckRoute(final Router router, final LogErrorHandler errorHandler) { router .route(HttpMethod.GET, UPCHECK_PATH) - .produces(JSON) + .produces(TEXT_PLAIN) .handler(new BlockingHandlerDecorator(new UpcheckHandler(), false)) .failureHandler(errorHandler); } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/ArtifactType.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/ArtifactType.java index b9c99f6cb..d4f0982a6 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/http/ArtifactType.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/ArtifactType.java @@ -24,5 +24,6 @@ public enum ArtifactType { SYNC_COMMITTEE_MESSAGE, SYNC_COMMITTEE_SELECTION_PROOF, SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF, - VALIDATOR_REGISTRATION + VALIDATOR_REGISTRATION, + BLOB_SIDECAR } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/SigningObjectMapperFactory.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/SigningObjectMapperFactory.java index 0243dce4d..9864d1f87 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/http/SigningObjectMapperFactory.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/SigningObjectMapperFactory.java @@ -14,6 +14,7 @@ import tech.pegasys.teku.api.schema.BLSPubKey; import tech.pegasys.teku.api.schema.BLSSignature; +import tech.pegasys.teku.api.schema.KZGCommitment; import tech.pegasys.teku.infrastructure.bytes.Bytes20; import tech.pegasys.teku.infrastructure.bytes.Bytes4; import tech.pegasys.teku.infrastructure.jackson.deserializers.bytes.Bytes32Deserializer; @@ -30,13 +31,18 @@ import tech.pegasys.teku.provider.BLSPubKeySerializer; import tech.pegasys.teku.provider.BLSSignatureDeserializer; import tech.pegasys.teku.provider.BLSSignatureSerializer; +import tech.pegasys.teku.provider.KZGCommitmentDeserializer; +import tech.pegasys.teku.provider.KZGCommitmentSerializer; import tech.pegasys.teku.provider.SszBitvectorSerializer; import tech.pegasys.web3signer.common.JacksonSerializers.HexDeserialiser; import tech.pegasys.web3signer.common.JacksonSerializers.HexSerialiser; import tech.pegasys.web3signer.common.JacksonSerializers.StringUInt64Deserializer; import tech.pegasys.web3signer.common.JacksonSerializers.StringUInt64Serialiser; import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.BlockRequest; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.KZGProof; import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.json.BlockRequestDeserializer; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.json.KZGProofDeserializer; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.json.KZGProofSerializer; import tech.pegasys.web3signer.signing.config.metadata.parser.SigningMetadataModule; import tech.pegasys.web3signer.signing.config.metadata.parser.SigningMetadataModule.Bytes32Serializer; @@ -98,6 +104,11 @@ private Module web3SignerMappers() { module.addDeserializer(Bytes20.class, new SigningMetadataModule.Bytes20Deserializer()); module.addSerializer(Bytes20.class, new SigningMetadataModule.Bytes20Serializer()); + module.addSerializer(KZGCommitment.class, new KZGCommitmentSerializer()); + module.addDeserializer(KZGCommitment.class, new KZGCommitmentDeserializer()); + + module.addSerializer(KZGProof.class, new KZGProofSerializer()); + module.addDeserializer(KZGProof.class, new KZGProofDeserializer()); return module; } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/BlobSidecar.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/BlobSidecar.java new file mode 100644 index 000000000..f6c720893 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/BlobSidecar.java @@ -0,0 +1,68 @@ +/* + * Copyright 2023 ConsenSys AG. + * + * 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.web3signer.core.service.http.handlers.signing.eth2; + +import tech.pegasys.teku.api.schema.KZGCommitment; +import tech.pegasys.teku.infrastructure.unsigned.UInt64; +import tech.pegasys.teku.spec.SpecVersion; +import tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlindedBlobSidecarSchema; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.common.annotations.VisibleForTesting; +import org.apache.tuweni.bytes.Bytes32; + +/** + * Json representation of BlindedBlobSidecar and BlobSidecar. The blob_root is the "hash tree root" + * of BlobSidecar's blob + */ +public record BlobSidecar( + @JsonProperty("block_root") Bytes32 blockRoot, + @JsonProperty("index") UInt64 index, + @JsonProperty("slot") UInt64 slot, + @JsonProperty("block_parent_root") Bytes32 blockParentRoot, + @JsonProperty("proposer_index") UInt64 proposerIndex, + @JsonProperty("blob_root") Bytes32 blobRoot, + @JsonProperty("kzg_commitment") KZGCommitment kzgCommitment, + @JsonProperty("kzg_proof") KZGProof kzgProof) { + + public tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlindedBlobSidecar + asInternalBlindedBlobSidecar(final SpecVersion spec) { + final BlindedBlobSidecarSchema blindedBlobSidecarSchema = + spec.getSchemaDefinitions().toVersionDeneb().orElseThrow().getBlindedBlobSidecarSchema(); + return new tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlindedBlobSidecar( + blindedBlobSidecarSchema, + blockRoot, + index, + slot, + blockParentRoot, + proposerIndex, + blobRoot, + kzgCommitment.asInternalKZGCommitment(), + kzgProof.asInternalKZGProof()); + } + + @VisibleForTesting + public static BlobSidecar fromInternalBlobSidecar( + final tech.pegasys.teku.spec.datastructures.blobs.versions.deneb.BlobSidecar sidecar) { + return new BlobSidecar( + sidecar.getBlockRoot(), + sidecar.getIndex(), + sidecar.getSlot(), + sidecar.getBlockParentRoot(), + sidecar.getProposerIndex(), + // convert blob to hash tree root to make it assignable to W3S BlobSidecar + sidecar.getBlob().hashTreeRoot(), + new KZGCommitment(sidecar.getKZGCommitment()), + new KZGProof(sidecar.getKZGProof())); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SignForIdentifierHandler.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SignForIdentifierHandler.java index 9bfbfabea..15a64f7de 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SignForIdentifierHandler.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SignForIdentifierHandler.java @@ -98,11 +98,11 @@ public void handle(final RoutingContext routingContext) { } final Bytes signingRoot = computeSigningRoot(eth2SigningRequestBody); - if (eth2SigningRequestBody.getSigningRoot() != null) { + if (eth2SigningRequestBody.signingRoot() != null) { checkArgument( - eth2SigningRequestBody.getSigningRoot().equals(signingRoot), + eth2SigningRequestBody.signingRoot().equals(signingRoot), "Signing root %s must match signing computed signing root %s from data", - eth2SigningRequestBody.getSigningRoot(), + eth2SigningRequestBody.signingRoot(), signingRoot); } @@ -170,8 +170,8 @@ private boolean maySign( final Bytes publicKey, final Bytes signingRoot, final Eth2SigningRequestBody eth2SigningRequestBody) { - final ForkInfo forkInfo = eth2SigningRequestBody.getForkInfo(); - switch (eth2SigningRequestBody.getType()) { + final ForkInfo forkInfo = eth2SigningRequestBody.forkInfo(); + switch (eth2SigningRequestBody.type()) { case BLOCK: case BLOCK_V2: try (final TimingContext ignored = @@ -180,7 +180,7 @@ private boolean maySign( publicKey, signingRoot, getBlockSlot(eth2SigningRequestBody), forkInfo); } case ATTESTATION: - final AttestationData attestation = eth2SigningRequestBody.getAttestation(); + final AttestationData attestation = eth2SigningRequestBody.attestation(); try (final TimingContext ignored = slashingMetrics.getDatabaseTimer().labels("attestation").startTimer()) { return slashingProtection @@ -199,10 +199,10 @@ private boolean maySign( private UInt64 getBlockSlot(final Eth2SigningRequestBody eth2SigningRequestBody) { final UInt64 blockSlot; - if (eth2SigningRequestBody.getType() == ArtifactType.BLOCK) { - blockSlot = UInt64.valueOf(eth2SigningRequestBody.getBlock().slot.bigIntegerValue()); + if (eth2SigningRequestBody.type() == ArtifactType.BLOCK) { + blockSlot = UInt64.valueOf(eth2SigningRequestBody.block().slot.bigIntegerValue()); } else { - final BlockRequest blockRequest = eth2SigningRequestBody.getBlockRequest(); + final BlockRequest blockRequest = eth2SigningRequestBody.blockRequest(); switch (blockRequest.getVersion()) { case PHASE0: case ALTAIR: @@ -229,64 +229,61 @@ private boolean maySignBlock( } private Bytes computeSigningRoot(final Eth2SigningRequestBody body) { - switch (body.getType()) { + switch (body.type()) { case BLOCK: - checkArgument(body.getBlock() != null, "block must be specified"); + checkArgument(body.block() != null, "block must be specified"); return signingRootUtil.signingRootForSignBlock( - body.getBlock().asInternalBeaconBlock(eth2Spec), - body.getForkInfo().asInternalForkInfo()); + body.block().asInternalBeaconBlock(eth2Spec), body.forkInfo().asInternalForkInfo()); case BLOCK_V2: - checkArgument(body.getBlockRequest() != null, "beacon_block must be specified"); + checkArgument(body.blockRequest() != null, "beacon_block must be specified"); final Bytes blockV2SigningRoot; - switch (body.getBlockRequest().getVersion()) { + switch (body.blockRequest().getVersion()) { case PHASE0: case ALTAIR: blockV2SigningRoot = signingRootUtil.signingRootForSignBlock( - body.getBlockRequest().getBeaconBlock().asInternalBeaconBlock(eth2Spec), - body.getForkInfo().asInternalForkInfo()); + body.blockRequest().getBeaconBlock().asInternalBeaconBlock(eth2Spec), + body.forkInfo().asInternalForkInfo()); break; case BELLATRIX: default: blockV2SigningRoot = signingRootUtil.signingRootForSignBlockHeader( - body.getBlockRequest().getBeaconBlockHeader().asInternalBeaconBlockHeader(), - body.getForkInfo().asInternalForkInfo()); + body.blockRequest().getBeaconBlockHeader().asInternalBeaconBlockHeader(), + body.forkInfo().asInternalForkInfo()); break; } return blockV2SigningRoot; case ATTESTATION: - checkArgument(body.getAttestation() != null, "attestation must be specified"); + checkArgument(body.attestation() != null, "attestation must be specified"); return signingRootUtil.signingRootForSignAttestationData( - body.getAttestation().asInternalAttestationData(), - body.getForkInfo().asInternalForkInfo()); + body.attestation().asInternalAttestationData(), body.forkInfo().asInternalForkInfo()); case AGGREGATE_AND_PROOF: - checkArgument(body.getAggregateAndProof() != null, "aggregateAndProof must be specified"); + checkArgument(body.aggregateAndProof() != null, "aggregateAndProof must be specified"); return signingRootUtil.signingRootForSignAggregateAndProof( - body.getAggregateAndProof().asInternalAggregateAndProof(eth2Spec), - body.getForkInfo().asInternalForkInfo()); + body.aggregateAndProof().asInternalAggregateAndProof(eth2Spec), + body.forkInfo().asInternalForkInfo()); case AGGREGATION_SLOT: - checkArgument(body.getAggregationSlot() != null, "aggregationSlot must be specified"); + checkArgument(body.aggregationSlot() != null, "aggregationSlot must be specified"); return signingRootUtil.signingRootForSignAggregationSlot( - body.getAggregationSlot().getSlot(), body.getForkInfo().asInternalForkInfo()); + body.aggregationSlot().getSlot(), body.forkInfo().asInternalForkInfo()); case RANDAO_REVEAL: - checkArgument(body.getRandaoReveal() != null, "randaoReveal must be specified"); + checkArgument(body.randaoReveal() != null, "randaoReveal must be specified"); return signingRootUtil.signingRootForRandaoReveal( - body.getRandaoReveal().getEpoch(), body.getForkInfo().asInternalForkInfo()); + body.randaoReveal().getEpoch(), body.forkInfo().asInternalForkInfo()); case VOLUNTARY_EXIT: - checkArgument(body.getVoluntaryExit() != null, "voluntaryExit must be specified"); + checkArgument(body.voluntaryExit() != null, "voluntaryExit must be specified"); return signingRootUtil.signingRootForSignVoluntaryExit( - body.getVoluntaryExit().asInternalVoluntaryExit(), - body.getForkInfo().asInternalForkInfo()); + body.voluntaryExit().asInternalVoluntaryExit(), body.forkInfo().asInternalForkInfo()); case DEPOSIT: - checkArgument(body.getDeposit() != null, "deposit must be specified"); + checkArgument(body.deposit() != null, "deposit must be specified"); final Bytes32 depositDomain = - computeDomain(Domain.DEPOSIT, body.getDeposit().getGenesisForkVersion(), Bytes32.ZERO); + computeDomain(Domain.DEPOSIT, body.deposit().getGenesisForkVersion(), Bytes32.ZERO); return DepositSigningRootUtil.computeSigningRoot( - body.getDeposit().asInternalDepositMessage(), depositDomain); + body.deposit().asInternalDepositMessage(), depositDomain); case SYNC_COMMITTEE_MESSAGE: - final SyncCommitteeMessage syncCommitteeMessage = body.getSyncCommitteeMessage(); + final SyncCommitteeMessage syncCommitteeMessage = body.syncCommitteeMessage(); checkArgument(syncCommitteeMessage != null, "SyncCommitteeMessage must be specified"); return signingRootFromSyncCommitteeUtils( syncCommitteeMessage.getSlot(), @@ -294,10 +291,10 @@ private Bytes computeSigningRoot(final Eth2SigningRequestBody body) { utils.getSyncCommitteeMessageSigningRoot( syncCommitteeMessage.getBeaconBlockRoot(), eth2Spec.computeEpochAtSlot(syncCommitteeMessage.getSlot()), - body.getForkInfo().asInternalForkInfo())); + body.forkInfo().asInternalForkInfo())); case SYNC_COMMITTEE_SELECTION_PROOF: final SyncAggregatorSelectionData syncAggregatorSelectionData = - body.getSyncAggregatorSelectionData(); + body.syncAggregatorSelectionData(); checkArgument( syncAggregatorSelectionData != null, "SyncAggregatorSelectionData is required"); return signingRootFromSyncCommitteeUtils( @@ -305,23 +302,31 @@ private Bytes computeSigningRoot(final Eth2SigningRequestBody body) { utils -> utils.getSyncAggregatorSelectionDataSigningRoot( asInternalSyncAggregatorSelectionData(syncAggregatorSelectionData), - body.getForkInfo().asInternalForkInfo())); + body.forkInfo().asInternalForkInfo())); case SYNC_COMMITTEE_CONTRIBUTION_AND_PROOF: - final ContributionAndProof contributionAndProof = body.getContributionAndProof(); + final ContributionAndProof contributionAndProof = body.contributionAndProof(); checkArgument(contributionAndProof != null, "ContributionAndProof is required"); return signingRootFromSyncCommitteeUtils( contributionAndProof.contribution.slot, utils -> utils.getContributionAndProofSigningRoot( asInternalContributionAndProof(contributionAndProof), - body.getForkInfo().asInternalForkInfo())); + body.forkInfo().asInternalForkInfo())); case VALIDATOR_REGISTRATION: - final ValidatorRegistration validatorRegistration = body.getValidatorRegistration(); + final ValidatorRegistration validatorRegistration = body.validatorRegistration(); checkArgument(validatorRegistration != null, "ValidatorRegistration is required"); return signingRootUtil.signingRootForValidatorRegistration( validatorRegistration.asInternalValidatorRegistration()); + case BLOB_SIDECAR: + // handles both blinded/blob sidecar + final BlobSidecar blobSidecar = body.blobSidecar(); + checkArgument(blobSidecar != null, "BlobSidecar is required"); + return signingRootUtil.signingRootForBlindedBlobSidecar( + blobSidecar.asInternalBlindedBlobSidecar(eth2Spec.atSlot(blobSidecar.slot())), + body.forkInfo().asInternalForkInfo()); + default: - throw new IllegalStateException("Signing root unimplemented for type " + body.getType()); + throw new IllegalStateException("Signing root unimplemented for type " + body.type()); } } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SigningRequestBody.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SigningRequestBody.java index be3dbfc0d..256946287 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SigningRequestBody.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/Eth2SigningRequestBody.java @@ -20,135 +20,24 @@ import tech.pegasys.web3signer.core.service.http.ArtifactType; import com.fasterxml.jackson.annotation.JsonAlias; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.tuweni.bytes.Bytes; -public class Eth2SigningRequestBody { - private final ArtifactType type; - private final Bytes signingRoot; - private final ForkInfo forkInfo; - private final BeaconBlock beaconBlock; // phase 0 - private final BlockRequest blockRequest; // altair and onward - private final AttestationData attestation; - private final AggregationSlot aggregationSlot; - private final AggregateAndProof aggregateAndProof; - private final VoluntaryExit voluntaryExit; - private final RandaoReveal randaoReveal; - private final DepositMessage deposit; - private final SyncCommitteeMessage syncCommitteeMessage; - private final SyncAggregatorSelectionData syncAggregatorSelectionData; - private final ContributionAndProof contributionAndProof; - private final ValidatorRegistration validatorRegistration; - - @JsonCreator - public Eth2SigningRequestBody( - @JsonProperty(value = "type", required = true) final ArtifactType type, - @JsonProperty("signing_root") @JsonAlias("signingRoot") final Bytes signingRoot, - @JsonProperty("fork_info") final ForkInfo forkInfo, - @JsonProperty("block") final BeaconBlock block, - @JsonProperty("beacon_block") final BlockRequest blockRequest, - @JsonProperty("attestation") final AttestationData attestation, - @JsonProperty("aggregation_slot") final AggregationSlot aggregationSlot, - @JsonProperty("aggregate_and_proof") final AggregateAndProof aggregateAndProof, - @JsonProperty("voluntary_exit") final VoluntaryExit voluntaryExit, - @JsonProperty("randao_reveal") final RandaoReveal randaoReveal, - @JsonProperty("deposit") final DepositMessage deposit, - @JsonProperty("sync_committee_message") final SyncCommitteeMessage syncCommitteeMessage, - @JsonProperty("sync_aggregator_selection_data") - final SyncAggregatorSelectionData syncAggregatorSelectionData, - @JsonProperty("contribution_and_proof") final ContributionAndProof contributionAndProof, - @JsonProperty("validator_registration") final ValidatorRegistration validatorRegistration) { - this.type = type; - this.signingRoot = signingRoot; - this.forkInfo = forkInfo; - this.beaconBlock = block; - this.blockRequest = blockRequest; - this.attestation = attestation; - this.aggregationSlot = aggregationSlot; - this.aggregateAndProof = aggregateAndProof; - this.voluntaryExit = voluntaryExit; - this.randaoReveal = randaoReveal; - this.deposit = deposit; - this.syncCommitteeMessage = syncCommitteeMessage; - this.syncAggregatorSelectionData = syncAggregatorSelectionData; - this.contributionAndProof = contributionAndProof; - this.validatorRegistration = validatorRegistration; - } - - @JsonProperty("type") - public ArtifactType getType() { - return type; - } - - @JsonProperty("fork_info") - public ForkInfo getForkInfo() { - return forkInfo; - } - - @JsonProperty("block") - public BeaconBlock getBlock() { - return beaconBlock; - } - - @JsonProperty("beacon_block") - public BlockRequest getBlockRequest() { - return blockRequest; - } - - @JsonProperty("attestation") - public AttestationData getAttestation() { - return attestation; - } - - @JsonProperty("signing_root") - @JsonAlias("signingRoot") - public Bytes getSigningRoot() { - return signingRoot; - } - - @JsonProperty("aggregation_slot") - public AggregationSlot getAggregationSlot() { - return aggregationSlot; - } - - @JsonProperty("aggregate_and_proof") - public AggregateAndProof getAggregateAndProof() { - return aggregateAndProof; - } - - @JsonProperty("voluntary_exit") - public VoluntaryExit getVoluntaryExit() { - return voluntaryExit; - } - - @JsonProperty("randao_reveal") - public RandaoReveal getRandaoReveal() { - return randaoReveal; - } - - @JsonProperty("deposit") - public DepositMessage getDeposit() { - return deposit; - } - - @JsonProperty("sync_committee_message") - public SyncCommitteeMessage getSyncCommitteeMessage() { - return syncCommitteeMessage; - } - - @JsonProperty("sync_aggregator_selection_data") - public SyncAggregatorSelectionData getSyncAggregatorSelectionData() { - return syncAggregatorSelectionData; - } - - @JsonProperty("contribution_and_proof") - public ContributionAndProof getContributionAndProof() { - return contributionAndProof; - } - - @JsonProperty("validator_registration") - public ValidatorRegistration getValidatorRegistration() { - return validatorRegistration; - } -} +public record Eth2SigningRequestBody( + @JsonProperty(value = "type", required = true) ArtifactType type, + @JsonProperty("signing_root") @JsonAlias("signingRoot") Bytes signingRoot, + @JsonProperty("fork_info") ForkInfo forkInfo, + @JsonProperty("block") BeaconBlock block, + @JsonProperty("beacon_block") BlockRequest blockRequest, + @JsonProperty("attestation") AttestationData attestation, + @JsonProperty("aggregation_slot") AggregationSlot aggregationSlot, + @JsonProperty("aggregate_and_proof") AggregateAndProof aggregateAndProof, + @JsonProperty("voluntary_exit") VoluntaryExit voluntaryExit, + @JsonProperty("randao_reveal") RandaoReveal randaoReveal, + @JsonProperty("deposit") DepositMessage deposit, + @JsonProperty("sync_committee_message") SyncCommitteeMessage syncCommitteeMessage, + @JsonProperty("sync_aggregator_selection_data") + SyncAggregatorSelectionData syncAggregatorSelectionData, + @JsonProperty("contribution_and_proof") ContributionAndProof contributionAndProof, + @JsonProperty("validator_registration") ValidatorRegistration validatorRegistration, + @JsonProperty("blob_sidecar") BlobSidecar blobSidecar) {} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/KZGProof.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/KZGProof.java new file mode 100644 index 000000000..b6470c06c --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/KZGProof.java @@ -0,0 +1,86 @@ +/* + * Copyright 2023 ConsenSys AG. + * + * 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.web3signer.core.service.http.handlers.signing.eth2; + +import static com.google.common.base.Preconditions.checkArgument; + +import java.util.Objects; + +import org.apache.tuweni.bytes.Bytes; + +public class KZGProof { + /** The number of bytes in this value - i.e. 48 */ + private static final int SIZE = 48; + + private final Bytes bytes; + + public KZGProof(final Bytes bytes) { + checkArgument( + bytes.size() == SIZE, + "Bytes%s should be %s bytes, but was %s bytes.", + SIZE, + SIZE, + bytes.size()); + this.bytes = bytes; + } + + public KZGProof(final tech.pegasys.teku.kzg.KZGProof kzgProof) { + this(kzgProof.toSSZBytes()); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final KZGProof other = (KZGProof) o; + return bytes.equals(other.bytes); + } + + @Override + public int hashCode() { + return Objects.hash(bytes); + } + + @Override + public String toString() { + return bytes.toString(); + } + + public static KZGProof fromHexString(final String value) { + try { + return new KZGProof(tech.pegasys.teku.kzg.KZGProof.fromHexString(value)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("KZGProof " + value + " is invalid: " + e.getMessage(), e); + } + } + + public String toHexString() { + return bytes.toHexString(); + } + + public Bytes toBytes() { + return bytes; + } + + public static KZGProof empty() { + return new KZGProof(Bytes.wrap(new byte[SIZE])); + } + + public tech.pegasys.teku.kzg.KZGProof asInternalKZGProof() { + return tech.pegasys.teku.kzg.KZGProof.fromSSZBytes(bytes); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/json/KZGProofDeserializer.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/json/KZGProofDeserializer.java new file mode 100644 index 000000000..74e21dcb4 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/json/KZGProofDeserializer.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 ConsenSys AG. + * + * 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.web3signer.core.service.http.handlers.signing.eth2.json; + +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.KZGProof; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import org.apache.tuweni.bytes.Bytes; + +public class KZGProofDeserializer extends JsonDeserializer { + @Override + public KZGProof deserialize(final JsonParser p, final DeserializationContext ignore) + throws IOException { + return new KZGProof(Bytes.fromHexString(p.getValueAsString())); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/json/KZGProofSerializer.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/json/KZGProofSerializer.java new file mode 100644 index 000000000..e212f2d79 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/signing/eth2/json/KZGProofSerializer.java @@ -0,0 +1,31 @@ +/* + * Copyright 2023 ConsenSys AG. + * + * 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.web3signer.core.service.http.handlers.signing.eth2.json; + +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.KZGProof; + +import java.io.IOException; +import java.util.Locale; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +public class KZGProofSerializer extends JsonSerializer { + @Override + public void serialize( + final KZGProof value, final JsonGenerator gen, final SerializerProvider serializers) + throws IOException { + gen.writeString(value.toHexString().toLowerCase(Locale.ROOT)); + } +}