Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Capella changes #722

Merged
merged 5 commits into from
Feb 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## Next release
### Features Added
-
- Add Block signing support for Capella milestone

### Bugs fixed
- Upgrade to Vertx 4.3.8 to address CVE-2023-24815
-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import tech.pegasys.web3signer.core.service.jsonrpc.FilecoinSignature;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -116,7 +117,7 @@ public Boolean walletVerify(
public String executeRawJsonRpcRequest(final String request) throws IOException {
final String url = getUrl() + rpcPath;
final HttpPost post = new HttpPost(url);
post.setEntity(new StringEntity(request, Charsets.UTF_8));
post.setEntity(new StringEntity(request, StandardCharsets.UTF_8));
post.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.JSON_UTF_8.toString());
AUTH_TOKEN.ifPresent(token -> post.setHeader("Authorization", "Bearer " + token));
try (final CloseableHttpClient httpClient = HttpClients.createDefault();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class SignerConfiguration {
private final Optional<Long> altairForkEpoch;

private final Optional<Long> bellatrixForkEpoch;
private final Optional<Long> capellaForkEpoch;
private final Optional<String> network;
private final boolean keyManagerApiEnabled;
private Optional<WatermarkRepairParameters> watermarkRepairParameters;
Expand Down Expand Up @@ -104,6 +105,7 @@ public SignerConfiguration(
final Optional<Path> slashingDbPoolConfigurationFile,
final Optional<Long> altairForkEpoch,
final Optional<Long> bellatrixForkEpoch,
final Optional<Long> capellaForkEpoch,
final Optional<String> network,
final boolean keyManagerApiEnabled,
final Optional<WatermarkRepairParameters> watermarkRepairParameters) {
Expand Down Expand Up @@ -141,6 +143,7 @@ public SignerConfiguration(
this.slashingProtectionDbPoolConfigurationFile = slashingDbPoolConfigurationFile;
this.altairForkEpoch = altairForkEpoch;
this.bellatrixForkEpoch = bellatrixForkEpoch;
this.capellaForkEpoch = capellaForkEpoch;
this.network = network;
this.keyManagerApiEnabled = keyManagerApiEnabled;
this.watermarkRepairParameters = watermarkRepairParameters;
Expand Down Expand Up @@ -282,6 +285,10 @@ public Optional<Long> getBellatrixForkEpoch() {
return bellatrixForkEpoch;
}

public Optional<Long> getCapellaForkEpoch() {
return capellaForkEpoch;
}

public Optional<String> getNetwork() {
return network;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public class SignerConfigurationBuilder {
private long slashingPruningInterval = 1;
private Long altairForkEpoch = null;
private Long bellatrixForkEpoch = null;
private Long capellaForkEpoch = null;
private String network = null;
private boolean keyManagerApiEnabled = false;
private KeystoresParameters keystoresParameters;
Expand Down Expand Up @@ -243,6 +244,11 @@ public SignerConfigurationBuilder withBellatrixForkEpoch(final long bellatrixFor
return this;
}

public SignerConfigurationBuilder withCapellaForkEpoch(final long capellaForkEpoch) {
this.capellaForkEpoch = capellaForkEpoch;
return this;
}

public SignerConfigurationBuilder withNetwork(final String network) {
this.network = network;
return this;
Expand Down Expand Up @@ -307,6 +313,7 @@ public SignerConfiguration build() {
Optional.ofNullable(slashingProtectionDbPoolConfigurationFile),
Optional.ofNullable(altairForkEpoch),
Optional.ofNullable(bellatrixForkEpoch),
Optional.ofNullable(capellaForkEpoch),
Optional.ofNullable(network),
keyManagerApiEnabled,
Optional.ofNullable(watermarkRepairParameters));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,14 @@ private String createEth2SlashingProtectionArgs() {
signerConfig.getBellatrixForkEpoch().get()));
}

if (signerConfig.getCapellaForkEpoch().isPresent()) {
yamlConfig.append(
String.format(
YAML_NUMERIC_FMT,
"eth2.Xnetwork-capella-fork-epoch",
signerConfig.getCapellaForkEpoch().get()));
}

if (signerConfig.getNetwork().isPresent()) {
yamlConfig.append(
String.format(YAML_STRING_FMT, "eth2.network", signerConfig.getNetwork().get()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ private Collection<String> createEth2Args() {
params.add(Long.toString(signerConfig.getBellatrixForkEpoch().get()));
}

if (signerConfig.getCapellaForkEpoch().isPresent()) {
params.add("--Xnetwork-capella-fork-epoch");
params.add(Long.toString(signerConfig.getCapellaForkEpoch().get()));
}

if (signerConfig.getNetwork().isPresent()) {
params.add("--network");
params.add(signerConfig.getNetwork().get());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
/*
* 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 static java.util.stream.Collectors.toList;

import tech.pegasys.teku.infrastructure.async.SafeFuture;
import tech.pegasys.teku.infrastructure.ssz.SszList;
import tech.pegasys.teku.infrastructure.ssz.schema.SszListSchema;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.Spec;
import tech.pegasys.teku.spec.SpecVersion;
import tech.pegasys.teku.spec.config.SpecConfigBellatrix;
import tech.pegasys.teku.spec.datastructures.blocks.BeaconBlock;
import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBody;
import tech.pegasys.teku.spec.datastructures.blocks.blockbody.BeaconBlockBodySchema;
import tech.pegasys.teku.spec.datastructures.execution.ExecutionPayload;
import tech.pegasys.teku.spec.datastructures.execution.versions.capella.Withdrawal;
import tech.pegasys.teku.spec.datastructures.operations.BlsToExecutionChange;
import tech.pegasys.teku.spec.datastructures.operations.SignedBlsToExecutionChange;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsBellatrix;
import tech.pegasys.teku.spec.schemas.SchemaDefinitionsCapella;
import tech.pegasys.teku.spec.util.DataStructureUtil;

import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;

import org.apache.tuweni.bytes.Bytes32;

/**
* This class provide different implementation of randomBeaconBlock than Teku's DataStructureUtil.
* Instead of using genesis schema definition, our implementation uses spec at slot number to derive
* the schema definition.
*/
public class DataStructureUtilAdapter {
private static final int MAX_EP_RANDOM_WITHDRAWALS = 4;
private final DataStructureUtil util;
private int seed = 92892824;
private final Spec spec;

public DataStructureUtilAdapter(final Spec spec) {
this.spec = spec;
util = new DataStructureUtil(spec);
}

public BeaconBlock randomBeaconBlock(final UInt64 slotNum) {
final UInt64 proposerIndex = util.randomUInt64();
final Bytes32 previousRoot = util.randomBytes32();
final Bytes32 stateRoot = util.randomBytes32();
final BeaconBlockBody body = randomBeaconBlockBody(slotNum);
return new BeaconBlock(
spec.atSlot(slotNum).getSchemaDefinitions().getBeaconBlockSchema(),
slotNum,
proposerIndex,
previousRoot,
stateRoot,
body);
}

private BeaconBlockBody randomBeaconBlockBody(final UInt64 slotNum) {
final BeaconBlockBodySchema<?> schema =
spec.atSlot(slotNum).getSchemaDefinitions().getBeaconBlockBodySchema();
return schema
.createBlockBody(
builder -> {
builder
.randaoReveal(util.randomSignature())
.eth1Data(util.randomEth1Data())
.graffiti(Bytes32.ZERO)
.proposerSlashings(
util.randomSszList(
schema.getProposerSlashingsSchema(), util::randomProposerSlashing, 1))
.attesterSlashings(
util.randomSszList(
schema.getAttesterSlashingsSchema(), util::randomAttesterSlashing, 1))
.attestations(
util.randomSszList(
schema.getAttestationsSchema(), util::randomAttestation, 3))
.deposits(
util.randomSszList(
schema.getDepositsSchema(), util::randomDepositWithoutIndex, 1))
.voluntaryExits(
util.randomSszList(
schema.getVoluntaryExitsSchema(), util::randomSignedVoluntaryExit, 1));
if (builder.supportsSyncAggregate()) {
builder.syncAggregate(util.randomSyncAggregateIfRequiredBySchema(schema));
}
if (builder.supportsExecutionPayload()) {
builder.executionPayload(
SafeFuture.completedFuture(randomExecutionPayload(spec.atSlot(slotNum))));
}
if (builder.supportsBlsToExecutionChanges()) {
builder.blsToExecutionChanges(
randomSignedBlsToExecutionChangesList(spec.atSlot(slotNum)));
}
if (builder.supportsKzgCommitments()) {
builder.blobKzgCommitments(
SafeFuture.completedFuture(util.randomSszKzgCommitmentList()));
}
})
.join();
}

private ExecutionPayload randomExecutionPayload(final SpecVersion specVersion) {
final SpecConfigBellatrix specConfigBellatrix =
SpecConfigBellatrix.required(specVersion.getConfig());
return SchemaDefinitionsBellatrix.required(specVersion.getSchemaDefinitions())
.getExecutionPayloadSchema()
.createExecutionPayload(
builder ->
builder
.parentHash(util.randomBytes32())
.feeRecipient(util.randomBytes20())
.stateRoot(util.randomBytes32())
.receiptsRoot(util.randomBytes32())
.logsBloom(util.randomBytes(specConfigBellatrix.getBytesPerLogsBloom()))
.prevRandao(util.randomBytes32())
.blockNumber(util.randomUInt64())
.gasLimit(util.randomUInt64())
.gasUsed(util.randomUInt64())
.timestamp(util.randomUInt64())
.extraData(util.randomBytes(specConfigBellatrix.getMaxExtraDataBytes()))
.baseFeePerGas(util.randomUInt256())
.blockHash(util.randomBytes32())
.transactions(util.randomExecutionPayloadTransactions())
.withdrawals(() -> randomExecutionPayloadWithdrawals(specVersion))
.excessDataGas(util::randomUInt256));
}

private List<Withdrawal> randomExecutionPayloadWithdrawals(final SpecVersion specVersion) {
return IntStream.rangeClosed(0, randomInt(MAX_EP_RANDOM_WITHDRAWALS))
.mapToObj(__ -> randomWithdrawal(specVersion))
.collect(toList());
}

private Withdrawal randomWithdrawal(final SpecVersion specVersion) {
return SchemaDefinitionsCapella.required(specVersion.getSchemaDefinitions())
.getWithdrawalSchema()
.create(
util.randomUInt64(),
util.randomValidatorIndex(),
util.randomBytes20(),
util.randomUInt64());
}

private SszList<SignedBlsToExecutionChange> randomSignedBlsToExecutionChangesList(
final SpecVersion specVersion) {
final SszListSchema<SignedBlsToExecutionChange, ?> signedBlsToExecutionChangeSchema =
SchemaDefinitionsCapella.required(specVersion.getSchemaDefinitions())
.getBeaconBlockBodySchema()
.toVersionCapella()
.orElseThrow()
.getBlsToExecutionChangesSchema();
final int maxBlsToExecutionChanges =
specVersion.getConfig().toVersionCapella().orElseThrow().getMaxBlsToExecutionChanges();

return util.randomSszList(
signedBlsToExecutionChangeSchema,
maxBlsToExecutionChanges,
() -> randomSignedBlsToExecutionChange(specVersion));
}

private SignedBlsToExecutionChange randomSignedBlsToExecutionChange(
final SpecVersion specVersion) {
return SchemaDefinitionsCapella.required(specVersion.getSchemaDefinitions())
.getSignedBlsToExecutionChangeSchema()
.create(randomBlsToExecutionChange(specVersion), util.randomSignature());
}

private BlsToExecutionChange randomBlsToExecutionChange(final SpecVersion specVersion) {
return SchemaDefinitionsCapella.required(specVersion.getSchemaDefinitions())
.getBlsToExecutionChangeSchema()
.create(util.randomValidatorIndex(), util.randomPublicKey(), util.randomBytes20());
}

private int randomInt(final int bound) {
return new Random(nextSeed()).nextInt(bound);
}

private int nextSeed() {
return seed++;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
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.web3signer.core.service.http.ArtifactType;
import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.BlockRequest;
import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.Eth2SigningRequestBody;
Expand All @@ -33,7 +32,7 @@

public class Eth2BlockSigningRequestUtil {
private final SpecMilestone specMilestone;
private final DataStructureUtil beaconBlockUtil;
private final DataStructureUtilAdapter beaconBlockUtil;
private final SigningRootUtil signingRootUtil;
private final ForkInfo tekuForkInfo;
private final Fork tekuFork;
Expand All @@ -44,7 +43,7 @@ public class Eth2BlockSigningRequestUtil {
public Eth2BlockSigningRequestUtil(final SpecMilestone specMilestone) {
final Spec spec = TestSpecFactory.createMinimal(specMilestone);
this.specMilestone = specMilestone;
beaconBlockUtil = new DataStructureUtil(spec);
beaconBlockUtil = new DataStructureUtilAdapter(spec);
signingRootUtil = new SigningRootUtil(spec);
tekuForkInfo = Eth2RequestUtils.forkInfo().asInternalForkInfo();
tekuFork = new Fork(tekuForkInfo.getFork());
Expand All @@ -58,7 +57,7 @@ public Eth2BlockSigningRequestUtil(final SpecMilestone specMilestone) {
public Eth2BlockSigningRequestUtil(
final Spec spec, final UInt64 forkEpoch, final UInt64 beaconBlockSlot) {
specMilestone = spec.atEpoch(forkEpoch).getMilestone();
beaconBlockUtil = new DataStructureUtil(spec);
beaconBlockUtil = new DataStructureUtilAdapter(spec);
signingRootUtil = new SigningRootUtil(spec);
tekuForkInfo = Eth2RequestUtils.forkInfo(forkEpoch.longValue()).asInternalForkInfo();
tekuFork = new Fork(tekuForkInfo.getFork());
Expand All @@ -75,6 +74,7 @@ public Eth2SigningRequestBody createBlockV2Request() {
case ALTAIR:
return createBlockV2Request(new BlockRequest(specMilestone, getBeaconBlock()));
case BELLATRIX:
case CAPELLA:
return createBlockV2Request(new BlockRequest(specMilestone, getBeaconBlockHeader()));
default:
throw new IllegalStateException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ void setup() {
METADATA_FILE_HELPERS.createUnencryptedYamlFileAt(keyConfigFile, PRIVATE_KEY, KeyType.BLS);
}

@ParameterizedTest
@ParameterizedTest(name = "#{index} - Sign and verify BlockV2 Signature for spec {0}")
@EnumSource(
value = SpecMilestone.class,
names = {"PHASE0", "ALTAIR", "BELLATRIX"})
names = {"PHASE0", "ALTAIR", "BELLATRIX", "CAPELLA"})
void signAndVerifyBlockV2Signature(final SpecMilestone specMilestone) throws Exception {
final Eth2BlockSigningRequestUtil util = new Eth2BlockSigningRequestUtil(specMilestone);

Expand Down Expand Up @@ -88,14 +88,18 @@ void signAndVerifyLegacyBlockSignature() throws Exception {
assertThat(signature).isEqualTo(expectedSignature.toBytesCompressed());
}

@Test
void emptyBlockRequestReturnsBadRequestStatus() throws JsonProcessingException {
final Eth2BlockSigningRequestUtil util =
new Eth2BlockSigningRequestUtil(SpecMilestone.BELLATRIX);
setupEth2Signer(Eth2Network.MINIMAL, SpecMilestone.BELLATRIX);
@ParameterizedTest(
name = "#{index} - Empty block request for spec {0} should return bad request status")
@EnumSource(
value = SpecMilestone.class,
names = {"PHASE0", "ALTAIR", "BELLATRIX", "CAPELLA"})
void emptyBlockRequestReturnsBadRequestStatus(final SpecMilestone specMilestone)
throws JsonProcessingException {
final Eth2BlockSigningRequestUtil util = new Eth2BlockSigningRequestUtil(specMilestone);
setupEth2Signer(Eth2Network.MINIMAL, specMilestone);

final Eth2SigningRequestBody request =
util.createBlockV2Request(new BlockRequest(SpecMilestone.BELLATRIX));
util.createBlockV2Request(new BlockRequest(specMilestone));
final Response response =
signer.eth2Sign(KEY_PAIR.getPublicKey().toString(), request, ContentType.JSON);

Expand Down
Loading