Skip to content

Commit

Permalink
Merge branch 'master' into add-high-watermark-metadata
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
  • Loading branch information
siladu committed Sep 8, 2023
2 parents cd69f99 + be75f36 commit 00bed32
Show file tree
Hide file tree
Showing 12 changed files with 431 additions and 36 deletions.
10 changes: 5 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Changelog

## Next Release

### Breaking Changes
- Eth2 Signing request body: deprecating `signingRoot` in favor of `signing_root` property. `signingRoot` will be removed in a future release.
## 23.9.0

### 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.
- Bulk load Ethereum v3 wallet files in eth1 mode.
- Eth2 Signing request body now supports both `signingRoot` and the `signing_root` property
- Add network configuration for Holesky testnet
- Add `eth_signTypedData` RPC method under the eth1 subcommand. [#893](https://github.com/Consensys/web3signer/pull/893)

### Bugs fixed
- Upcheck was using application/json accept headers instead text/plain accept headers
Expand Down
5 changes: 3 additions & 2 deletions acceptance-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ dependencies {
testImplementation 'tech.pegasys.teku.internal:networks'
testImplementation 'tech.pegasys.teku.internal:json'
testImplementation 'tech.pegasys.teku.internal:jackson'
testImplementation (group: 'tech.pegasys.teku.internal', name: 'spec', classifier: 'test-fixtures')
testImplementation (group: 'tech.pegasys.teku.internal', name: 'bls', classifier: 'test-fixtures')
// Use test fixtures from Teku 23.8.0 as latest version doesn't include any test fixtures
testImplementation (group: 'tech.pegasys.teku.internal', name: 'spec', classifier: 'test-fixtures', version: '23.8.0')
testImplementation (group: 'tech.pegasys.teku.internal', name: 'bls', classifier: 'test-fixtures', version: '23.8.0')
testImplementation 'tech.pegasys.teku.internal:serializer'
testImplementation 'tech.pegasys.teku.internal:unsigned'
testImplementation 'tech.pegasys.teku.internal:async'
Expand Down
5 changes: 1 addition & 4 deletions acceptance-tests/src/test/resources/eth2/network_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@ GOSSIP_MAX_SIZE: 10485760
# `2**10` (= 1024)
MAX_REQUEST_BLOCKS: 1024
## [customized] `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 272)
#MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272
# `10 * 2**20` (=10485760, 10 MiB)
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256
## [customized] `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 272)
#MIN_EPOCHS_FOR_BLOCK_REQUESTS: 272
# `10 * 2**20` (=10485760, 10 MiB)
MAX_CHUNK_SIZE: 10485760
# 5s
TTFB_TIMEOUT: 5
Expand Down
36 changes: 17 additions & 19 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ plugins {
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'me.champeau.gradle.jmh' version '0.5.3' apply false
id 'net.ltgt.errorprone' version '2.0.2'
id 'org.ajoberstar.grgit' version '4.1.1'
id 'org.ajoberstar.grgit' version '5.2.0'
}

if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
Expand All @@ -47,7 +47,6 @@ if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {

rootProject.version = calculatePublishVersion()
def specificVersion = calculateVersion()
def isDevelopBuild = rootProject.version.contains('develop')

group = 'tech.pegasys.web3signer'

Expand Down Expand Up @@ -506,22 +505,30 @@ task testDocker {
}
}

task uploadDocker {
dependsOn([distDocker])
def dockerBuildVersion = "${rootProject.version}".replace('+', '-')
def architecture = System.getenv('architecture')
def platform = System.getenv('platform')

def dockerTagPrefixes(String dockerBuildVersion) {
def versionPrefixes = [dockerBuildVersion]
if (project.hasProperty('branch') && project.property('branch') == 'master') {
versionPrefixes.add('develop')
}

if (!isDevelopBuild) {
// add `latest` tag for non-develop and non-rc versions
if (!rootProject.version.contains('develop') && !rootProject.version.toLowerCase().contains('-rc')) {
versionPrefixes.add('latest')
// when docker build version is x.y.[a,b..] we would also like to use tag x.y
versionPrefixes.add(dockerBuildVersion.split(/\./)[0..1].join('.'))
}

return versionPrefixes
}

task uploadDocker {
dependsOn([distDocker])
def dockerBuildVersion = "${rootProject.version}".replace('+', '-')
def architecture = System.getenv('architecture')
def platform = System.getenv('platform')

def versionPrefixes = dockerTagPrefixes(dockerBuildVersion)

doLast {
for (def variant in dockerJdkVariants) {
def tags = ""
Expand Down Expand Up @@ -557,18 +564,9 @@ task uploadDocker {

task manifestDocker {
def dockerBuildVersion = "${rootProject.version}".replace('+', '-')
def versionPrefixes = [dockerBuildVersion]
def versionPrefixes = dockerTagPrefixes(dockerBuildVersion)
def platforms = ["arm64", "amd64"]

if (project.hasProperty('branch') && project.property('branch') == 'master') {
versionPrefixes.add('develop')
}

if (!isDevelopBuild) {
versionPrefixes.add('latest')
versionPrefixes.add(dockerBuildVersion.split(/\./)[0..1].join('.'))
}

doLast {
for (def variant in dockerJdkVariants) {
def tags = []
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* 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.jsonrpcproxy;

import static java.util.Collections.singletonList;
import static org.web3j.crypto.Keys.getAddress;
import static tech.pegasys.web3signer.core.service.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT;

import tech.pegasys.web3signer.core.jsonrpcproxy.support.EthSignTypedData;
import tech.pegasys.web3signer.core.service.jsonrpc.response.JsonRpcErrorResponse;
import tech.pegasys.web3signer.core.service.jsonrpc.response.JsonRpcSuccessResponse;

import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Arrays;
import java.util.Map;

import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.json.Json;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.junit.jupiter.api.Test;
import org.web3j.crypto.Keys;
import org.web3j.protocol.core.Request;

public class EthSignTypedDataIntegrationTest extends IntegrationTestBase {

// Json taken and validated using https://metamask.github.io/test-dapp/#signTypedDataV4
private static final String eip712Json =
"""
{
"types": {
"EIP712Domain": [
{
"name": "name",
"type": "string"
},
{
"name": "version",
"type": "string"
},
{
"name": "chainId",
"type": "uint256"
},
{
"name": "verifyingContract",
"type": "address"
}
],
"Person": [
{
"name": "name",
"type": "string"
},
{
"name": "wallet",
"type": "address"
}
]
},
"domain": {
"name": "My Dapp",
"version": "1.0",
"chainId": 1,
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"primaryType": "Person",
"message": {
"name": "John Doe",
"wallet": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B"
}
}
""";

@Test
void ethSignTypedDataSignsDataWhenAnUnlockedAccountIsPassed() {
final Request<?, EthSignTypedData> requestBody =
new Request<>(
"eth_signTypedData",
Arrays.asList(unlockedAccount, eip712Json),
null,
EthSignTypedData.class);

final Iterable<Map.Entry<String, String>> expectedHeaders =
singletonList(ImmutablePair.of("Content", HttpHeaderValues.APPLICATION_JSON.toString()));

final JsonRpcSuccessResponse responseBody =
new JsonRpcSuccessResponse(
requestBody.getId(),
"0x11cb46f70ad43da86e15ca7c6bb28356859a5f4ba430b44dbf1e65726d467be6072be9d1e5b40bd5b7abe8888eb91a69f0e6d56a8a094718ed8080baf02d61c31c");

sendPostRequestAndVerifyResponse(
request.web3Signer(Json.encode(requestBody)),
response.web3Signer(expectedHeaders, Json.encode(responseBody)));
}

@Test
void ethSignTypedDataDoNotSignMessageWhenSignerAccountIsNotUnlocked()
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException {
final String A_RANDOM_ADDRESS = getAddress(Keys.createEcKeyPair().getPublicKey());

final Request<?, EthSignTypedData> requestBody =
new Request<>(
"eth_signTypedData",
Arrays.asList(A_RANDOM_ADDRESS, eip712Json),
null,
EthSignTypedData.class);

final Iterable<Map.Entry<String, String>> expectedHeaders =
singletonList(ImmutablePair.of("Content", HttpHeaderValues.APPLICATION_JSON.toString()));

final JsonRpcErrorResponse responseBody =
new JsonRpcErrorResponse(requestBody.getId(), SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT);

sendPostRequestAndVerifyResponse(
request.web3Signer(Json.encode(requestBody)),
response.web3Signer(expectedHeaders, Json.encode(responseBody)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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.jsonrpcproxy.support;

import org.web3j.protocol.core.Response;

public class EthSignTypedData extends Response<String> {
public String getSignature() {
return getResult();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.RequestMapper;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.internalresponse.EthSignResultProvider;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.internalresponse.EthSignTransactionResultProvider;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.internalresponse.EthSignTypedDataResultProvider;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.internalresponse.InternalResponseHandler;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.SendTransactionHandler;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.transaction.TransactionFactory;
Expand Down Expand Up @@ -306,6 +307,10 @@ private RequestMapper createRequestMapper(
requestMapper.addHandler(
"eth_sign",
new InternalResponseHandler<>(responseFactory, new EthSignResultProvider(secpSigner)));
requestMapper.addHandler(
"eth_signTypedData",
new InternalResponseHandler<>(
responseFactory, new EthSignTypedDataResultProvider(secpSigner)));
requestMapper.addHandler(
"eth_signTransaction",
new InternalResponseHandler<>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ private void registerUpcheckRoute(final Router router, final LogErrorHandler err
router
.route(HttpMethod.GET, UPCHECK_PATH)
.produces(TEXT_PLAIN)
.handler(new BlockingHandlerDecorator(new UpcheckHandler(), false))
.handler(new UpcheckHandler())
.failureHandler(errorHandler);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.jsonrpc.handlers.internalresponse;

import static tech.pegasys.web3signer.core.service.jsonrpc.response.JsonRpcError.INVALID_PARAMS;
import static tech.pegasys.web3signer.core.service.jsonrpc.response.JsonRpcError.SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT;
import static tech.pegasys.web3signer.signing.util.IdentifierUtils.normaliseIdentifier;

import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier;
import tech.pegasys.web3signer.core.service.jsonrpc.JsonRpcRequest;
import tech.pegasys.web3signer.core.service.jsonrpc.exceptions.JsonRpcException;
import tech.pegasys.web3signer.core.service.jsonrpc.handlers.ResultProvider;
import tech.pegasys.web3signer.signing.SecpArtifactSignature;

import java.io.IOException;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
import org.web3j.crypto.StructuredDataEncoder;

public class EthSignTypedDataResultProvider implements ResultProvider<String> {

private static final Logger LOG = LogManager.getLogger();

private final SignerForIdentifier<SecpArtifactSignature> transactionSignerProvider;

public EthSignTypedDataResultProvider(
final SignerForIdentifier<SecpArtifactSignature> transactionSignerProvider) {
this.transactionSignerProvider = transactionSignerProvider;
}

@Override
public String createResponseResult(final JsonRpcRequest request) {
final List<String> params = getParams(request);
if (params == null || params.size() != 2) {
LOG.debug(
"eth_signTypedData should have a list of 2 parameters, but has {}",
params == null ? "null" : params.size());
throw new JsonRpcException(INVALID_PARAMS);
}

final String eth1Address = params.get(0);
final String jsonData = params.get(1);

final StructuredDataEncoder dataEncoder;
try {
dataEncoder = new StructuredDataEncoder(jsonData);
} catch (IOException e) {
throw new RuntimeException("Exception thrown while enconding the json provided");
}
final Bytes structuredData = Bytes.of(dataEncoder.getStructuredData());
return transactionSignerProvider
.sign(normaliseIdentifier(eth1Address), structuredData)
.orElseThrow(
() -> {
LOG.debug("Address ({}) does not match any available account", eth1Address);
return new JsonRpcException(SIGNING_FROM_IS_NOT_AN_UNLOCKED_ACCOUNT);
});
}

private List<String> getParams(final JsonRpcRequest request) {
try {
@SuppressWarnings("unchecked")
final List<String> params = (List<String>) request.getParams();
return params;
} catch (final ClassCastException e) {
LOG.debug(
"eth_signTypedData should have a list of 2 parameters, but received an object: {}",
request.getParams());
throw new JsonRpcException(INVALID_PARAMS);
}
}
}
Loading

0 comments on commit 00bed32

Please sign in to comment.