diff --git a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/tls/support/TlsEnabledHttpServerFactory.java b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/tls/support/TlsEnabledHttpServerFactory.java index f1434b670..639327012 100644 --- a/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/tls/support/TlsEnabledHttpServerFactory.java +++ b/acceptance-tests/src/test/java/tech/pegasys/web3signer/dsl/tls/support/TlsEnabledHttpServerFactory.java @@ -12,9 +12,9 @@ */ package tech.pegasys.web3signer.dsl.tls.support; -import static tech.pegasys.web3signer.core.Eth1Runner.createJsonDecoder; import static tech.pegasys.web3signer.dsl.tls.support.CertificateHelpers.populateFingerprintFile; +import tech.pegasys.web3signer.core.service.jsonrpc.Eth1JsonDecoderFactory; import tech.pegasys.web3signer.core.service.jsonrpc.JsonDecoder; import tech.pegasys.web3signer.core.service.jsonrpc.handlers.HttpResponseFactory; import tech.pegasys.web3signer.core.service.jsonrpc.handlers.JsonRpcErrorHandler; @@ -81,7 +81,7 @@ public HttpServer create( .setPassword(serverCert.getPassword())); final Router router = Router.router(vertx); - final JsonDecoder jsonDecoder = createJsonDecoder(); + final JsonDecoder jsonDecoder = Eth1JsonDecoderFactory.create(); final RequestMapper requestMapper = new RequestMapper(new MockBalanceReporter()); router .route(HttpMethod.POST, "/") diff --git a/commandline/src/test/java/tech/pegasys/web3signer/commandline/CommandlineParserTest.java b/commandline/src/test/java/tech/pegasys/web3signer/commandline/CommandlineParserTest.java index d3edf8e41..7d54692f7 100644 --- a/commandline/src/test/java/tech/pegasys/web3signer/commandline/CommandlineParserTest.java +++ b/commandline/src/test/java/tech/pegasys/web3signer/commandline/CommandlineParserTest.java @@ -36,6 +36,7 @@ import java.io.StringWriter; import java.net.InetAddress; import java.util.Collections; +import java.util.List; import java.util.function.Supplier; import io.vertx.core.Vertx; @@ -643,9 +644,9 @@ protected NoOpRunner(final BaseConfig baseConfig) { public void run() {} @Override - protected ArtifactSignerProvider createArtifactSignerProvider( + protected List createArtifactSignerProvider( final Vertx vertx, final MetricsSystem metricsSystem) { - return new DefaultArtifactSignerProvider(Collections::emptyList); + return List.of(new DefaultArtifactSignerProvider(Collections::emptyList)); } @Override diff --git a/core/src/main/java/tech/pegasys/web3signer/core/Context.java b/core/src/main/java/tech/pegasys/web3signer/core/Context.java index c308894bd..440fc0667 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/Context.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/Context.java @@ -15,6 +15,8 @@ import tech.pegasys.web3signer.core.service.http.handlers.LogErrorHandler; import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import java.util.List; + import io.vertx.core.Vertx; import io.vertx.ext.web.Router; import org.hyperledger.besu.plugin.services.MetricsSystem; @@ -24,19 +26,19 @@ public class Context { private final MetricsSystem metricsSystem; private final LogErrorHandler errorHandler; private final Vertx vertx; - private final ArtifactSignerProvider artifactSignerProvider; + private final List artifactSignerProviders; public Context( final Router router, final MetricsSystem metricsSystem, final LogErrorHandler errorHandler, final Vertx vertx, - final ArtifactSignerProvider artifactSignerProvider) { + final List artifactSignerProviders) { this.router = router; this.metricsSystem = metricsSystem; this.errorHandler = errorHandler; this.vertx = vertx; - this.artifactSignerProvider = artifactSignerProvider; + this.artifactSignerProviders = artifactSignerProviders; } public Router getRouter() { @@ -55,7 +57,7 @@ public Vertx getVertx() { return vertx; } - public ArtifactSignerProvider getArtifactSignerProvider() { - return artifactSignerProvider; + public List getArtifactSignerProviders() { + return artifactSignerProviders; } } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java b/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java index e33bc4e6c..0fa92854e 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/Eth1Runner.java @@ -15,37 +15,19 @@ import static tech.pegasys.web3signer.core.config.HealthCheckNames.KEYS_CHECK_AWS_BULK_LOADING; import static tech.pegasys.web3signer.core.config.HealthCheckNames.KEYS_CHECK_AZURE_BULK_LOADING; import static tech.pegasys.web3signer.core.config.HealthCheckNames.KEYS_CHECK_V3_KEYSTORES_BULK_LOADING; -import static tech.pegasys.web3signer.signing.KeyType.SECP256K1; import tech.pegasys.web3signer.core.config.BaseConfig; import tech.pegasys.web3signer.core.config.Eth1Config; -import tech.pegasys.web3signer.core.service.DownstreamPathCalculator; -import tech.pegasys.web3signer.core.service.VertxRequestTransmitter; -import tech.pegasys.web3signer.core.service.VertxRequestTransmitterFactory; -import tech.pegasys.web3signer.core.service.http.handlers.LogErrorHandler; -import tech.pegasys.web3signer.core.service.http.handlers.signing.Eth1SignForIdentifierHandler; -import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier; -import tech.pegasys.web3signer.core.service.http.metrics.HttpApiMetrics; -import tech.pegasys.web3signer.core.service.jsonrpc.JsonDecoder; -import tech.pegasys.web3signer.core.service.jsonrpc.handlers.Eth1AccountsHandler; -import tech.pegasys.web3signer.core.service.jsonrpc.handlers.HttpResponseFactory; -import tech.pegasys.web3signer.core.service.jsonrpc.handlers.JsonRpcErrorHandler; -import tech.pegasys.web3signer.core.service.jsonrpc.handlers.JsonRpcHandler; -import tech.pegasys.web3signer.core.service.jsonrpc.handlers.PassThroughHandler; -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; +import tech.pegasys.web3signer.core.routes.PublicKeysListRoute; +import tech.pegasys.web3signer.core.routes.ReloadRoute; +import tech.pegasys.web3signer.core.routes.eth1.Eth1SignRoute; +import tech.pegasys.web3signer.core.routes.eth1.JsonRpcRoute; import tech.pegasys.web3signer.keystorage.azure.AzureKeyVault; import tech.pegasys.web3signer.keystorage.common.MappedResults; import tech.pegasys.web3signer.keystorage.hashicorp.HashicorpConnectionFactory; import tech.pegasys.web3signer.signing.ArtifactSigner; import tech.pegasys.web3signer.signing.ArtifactSignerProvider; import tech.pegasys.web3signer.signing.EthSecpArtifactSigner; -import tech.pegasys.web3signer.signing.SecpArtifactSignature; import tech.pegasys.web3signer.signing.bulkloading.SecpAwsBulkLoader; import tech.pegasys.web3signer.signing.bulkloading.SecpAzureBulkLoader; import tech.pegasys.web3signer.signing.bulkloading.SecpV3KeystoresBulkLoader; @@ -69,29 +51,26 @@ import java.util.Collections; import java.util.List; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; import io.vertx.core.Vertx; -import io.vertx.core.http.HttpClient; -import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonObject; import io.vertx.ext.healthchecks.Status; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.client.WebClientOptions; -import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.ext.web.handler.ResponseContentTypeHandler; -import io.vertx.ext.web.impl.BlockingHandlerDecorator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.hyperledger.besu.plugin.services.MetricsSystem; +/** + * Runner subclass for eth1 mode + * + *

eth1 mode uses two secp signer providers. For signing operations /api/v1/eth1/sign/, the + * secp256k1 primary key is used as identifier. For JSON-RPC call (eth_signTransactions etc.), the + * eth1 address is used as identifier. See createArtifactSignerProvider method. + * + * @see SecpArtifactSignerProviderAdapter that uses existing ArtifactSignerProvideer maps eth1 + * address to primary key. + */ public class Eth1Runner extends Runner { - public static final String PUBLIC_KEYS_PATH = "/api/v1/eth1/publicKeys"; - public static final String ROOT_PATH = "/"; - public static final String SIGN_PATH = "/api/v1/eth1/sign/:identifier"; private static final Logger LOG = LogManager.getLogger(); private final Eth1Config eth1Config; - private final HttpResponseFactory responseFactory = new HttpResponseFactory(); public Eth1Runner(final BaseConfig baseConfig, final Eth1Config eth1Config) { super(baseConfig); @@ -100,99 +79,49 @@ public Eth1Runner(final BaseConfig baseConfig, final Eth1Config eth1Config) { @Override protected void populateRouter(final Context context) { - final Router router = context.getRouter(); - final LogErrorHandler errorHandler = context.getErrorHandler(); - final ArtifactSignerProvider signerProvider = context.getArtifactSignerProvider(); - - addPublicKeysListHandler(router, signerProvider, PUBLIC_KEYS_PATH, context.getErrorHandler()); + new PublicKeysListRoute(context, "eth1").register(); + new Eth1SignRoute(context).register(); + new ReloadRoute(context).register(); + new JsonRpcRoute(context, eth1Config).register(); + } - final SignerForIdentifier secpSigner = - new SignerForIdentifier<>(signerProvider, this::formatSecpSignature, SECP256K1); - router - .route(HttpMethod.POST, SIGN_PATH) - .handler( - new BlockingHandlerDecorator( - new Eth1SignForIdentifierHandler( - secpSigner, - new HttpApiMetrics(context.getMetricsSystem(), SECP256K1, signerProvider)), - false)) - .failureHandler(errorHandler); + @Override + protected List createArtifactSignerProvider( + final Vertx vertx, final MetricsSystem metricsSystem) { + final ArtifactSignerProvider signerProvider = + new DefaultArtifactSignerProvider( + () -> { + final List signers = new ArrayList<>(); + final AzureKeyVaultFactory azureKeyVaultFactory = new AzureKeyVaultFactory(); + final AzureHttpClientFactory azureHttpClientFactory = new AzureHttpClientFactory(); + registerClose(azureKeyVaultFactory::close); + final AzureKeyVaultSignerFactory azureSignerFactory = + new AzureKeyVaultSignerFactory(azureKeyVaultFactory, azureHttpClientFactory); + final CachedAwsKmsClientFactory cachedAwsKmsClientFactory = + new CachedAwsKmsClientFactory(eth1Config.getAwsKmsClientCacheSize()); + final AwsKmsSignerFactory awsKmsSignerFactory = + new AwsKmsSignerFactory(cachedAwsKmsClientFactory, true); + signers.addAll( + loadSignersFromKeyConfigFiles( + vertx, azureKeyVaultFactory, azureSignerFactory, awsKmsSignerFactory) + .getValues()); + signers.addAll( + bulkLoadSigners( + azureKeyVaultFactory, + azureSignerFactory, + cachedAwsKmsClientFactory, + awsKmsSignerFactory) + .getValues()); + return signers; + }); + + // uses eth1 address as identifier final ArtifactSignerProvider secpArtifactSignerProvider = new SecpArtifactSignerProviderAdapter(signerProvider); - loadSignerProvider(secpArtifactSignerProvider); - - // The order of the elements in the list DO matter - addReloadHandler( - router, List.of(signerProvider, secpArtifactSignerProvider), context.getErrorHandler()); - - final DownstreamPathCalculator downstreamPathCalculator = - new DownstreamPathCalculator(eth1Config.getDownstreamHttpPath()); - - final WebClientOptions webClientOptions = - new WebClientOptionsFactory().createWebClientOptions(eth1Config); - final HttpClient downStreamConnection = context.getVertx().createHttpClient(webClientOptions); - - final VertxRequestTransmitterFactory transmitterFactory = - responseBodyHandler -> - new VertxRequestTransmitter( - context.getVertx(), - downStreamConnection, - eth1Config.getDownstreamHttpRequestTimeout(), - downstreamPathCalculator, - responseBodyHandler); - - final JsonDecoder jsonDecoder = createJsonDecoder(); - final PassThroughHandler passThroughHandler = - new PassThroughHandler(transmitterFactory, jsonDecoder); - - final RequestMapper requestMapper = - createRequestMapper( - transmitterFactory, - secpArtifactSignerProvider, - jsonDecoder, - eth1Config.getChainId().id()); - - router - .route(HttpMethod.POST, ROOT_PATH) - .produces(Runner.JSON) - .handler(ResponseContentTypeHandler.create()) - .handler(BodyHandler.create()) - .failureHandler(new JsonRpcErrorHandler(new HttpResponseFactory())) - .blockingHandler(new JsonRpcHandler(responseFactory, requestMapper, jsonDecoder), false); - - router.route().handler(BodyHandler.create()).handler(passThroughHandler); - } - - @Override - protected ArtifactSignerProvider createArtifactSignerProvider( - final Vertx vertx, final MetricsSystem metricsSystem) { - return new DefaultArtifactSignerProvider( - () -> { - final List signers = new ArrayList<>(); - final AzureKeyVaultFactory azureKeyVaultFactory = new AzureKeyVaultFactory(); - final AzureHttpClientFactory azureHttpClientFactory = new AzureHttpClientFactory(); - registerClose(azureKeyVaultFactory::close); - final AzureKeyVaultSignerFactory azureSignerFactory = - new AzureKeyVaultSignerFactory(azureKeyVaultFactory, azureHttpClientFactory); - final CachedAwsKmsClientFactory cachedAwsKmsClientFactory = - new CachedAwsKmsClientFactory(eth1Config.getAwsKmsClientCacheSize()); - final AwsKmsSignerFactory awsKmsSignerFactory = - new AwsKmsSignerFactory(cachedAwsKmsClientFactory, true); - signers.addAll( - loadSignersFromKeyConfigFiles( - vertx, azureKeyVaultFactory, azureSignerFactory, awsKmsSignerFactory) - .getValues()); - signers.addAll( - bulkLoadSigners( - azureKeyVaultFactory, - azureSignerFactory, - cachedAwsKmsClientFactory, - awsKmsSignerFactory) - .getValues()); - return signers; - }); + // this order DO matter for reload handler + return List.of(signerProvider, secpArtifactSignerProvider); } private MappedResults loadSignersFromKeyConfigFiles( @@ -309,66 +238,6 @@ private MappedResults bulkloadV3Keystores() { return walletResults; } - private String formatSecpSignature(final SecpArtifactSignature signature) { - return SecpArtifactSignature.toBytes(signature).toHexString(); - } - - public static JsonDecoder createJsonDecoder() { - // Force Transaction Deserialization to fail if missing expected properties - final ObjectMapper jsonObjectMapper = new ObjectMapper(); - jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true); - jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true); - - return new JsonDecoder(jsonObjectMapper); - } - - private RequestMapper createRequestMapper( - final VertxRequestTransmitterFactory transmitterFactory, - final ArtifactSignerProvider signerProviderMappedToEth1Address, - final JsonDecoder jsonDecoder, - final long chainId) { - final PassThroughHandler defaultHandler = - new PassThroughHandler(transmitterFactory, jsonDecoder); - final SignerForIdentifier secpSigner = - new SignerForIdentifier<>( - signerProviderMappedToEth1Address, this::formatSecpSignature, SECP256K1); - final TransactionFactory transactionFactory = - new TransactionFactory(chainId, jsonDecoder, transmitterFactory); - final SendTransactionHandler sendTransactionHandler = - new SendTransactionHandler(chainId, transactionFactory, transmitterFactory, secpSigner); - - final RequestMapper requestMapper = new RequestMapper(defaultHandler); - requestMapper.addHandler( - "eth_accounts", - new InternalResponseHandler<>( - responseFactory, - new Eth1AccountsHandler(signerProviderMappedToEth1Address::availableIdentifiers))); - 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<>( - responseFactory, - new EthSignTransactionResultProvider(chainId, secpSigner, jsonDecoder))); - requestMapper.addHandler("eth_sendTransaction", sendTransactionHandler); - requestMapper.addHandler("eea_sendTransaction", sendTransactionHandler); - - return requestMapper; - } - - private void loadSignerProvider(final ArtifactSignerProvider signerProvider) { - try { - signerProvider.load().get(); // wait for signers to get loaded ... - } catch (final Exception e) { - throw new InitializationException(e); - } - } - private void registerSignerLoadingHealthCheck( final String name, final MappedResults result) { super.registerHealthCheckProcedure( diff --git a/core/src/main/java/tech/pegasys/web3signer/core/Eth2Runner.java b/core/src/main/java/tech/pegasys/web3signer/core/Eth2Runner.java index e3ec76a12..e0807a591 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/Eth2Runner.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/Eth2Runner.java @@ -18,34 +18,24 @@ import static tech.pegasys.web3signer.core.config.HealthCheckNames.KEYS_CHECK_GCP_BULK_LOADING; import static tech.pegasys.web3signer.core.config.HealthCheckNames.KEYS_CHECK_KEYSTORE_BULK_LOADING; import static tech.pegasys.web3signer.core.config.HealthCheckNames.SLASHING_PROTECTION_DB; -import static tech.pegasys.web3signer.signing.KeyType.BLS; import tech.pegasys.teku.bls.BLSKeyPair; import tech.pegasys.teku.bls.BLSSecretKey; import tech.pegasys.teku.spec.Spec; import tech.pegasys.web3signer.core.config.BaseConfig; -import tech.pegasys.web3signer.core.metrics.SlashingProtectionMetrics; -import tech.pegasys.web3signer.core.service.http.SigningObjectMapperFactory; -import tech.pegasys.web3signer.core.service.http.handlers.HighWatermarkHandler; -import tech.pegasys.web3signer.core.service.http.handlers.LogErrorHandler; -import tech.pegasys.web3signer.core.service.http.handlers.keymanager.delete.DeleteKeystoresHandler; -import tech.pegasys.web3signer.core.service.http.handlers.keymanager.imports.ImportKeystoresHandler; -import tech.pegasys.web3signer.core.service.http.handlers.keymanager.list.ListKeystoresHandler; -import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier; -import tech.pegasys.web3signer.core.service.http.handlers.signing.SigningExtensionHandler; -import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.Eth2SignForIdentifierHandler; -import tech.pegasys.web3signer.core.service.http.metrics.HttpApiMetrics; +import tech.pegasys.web3signer.core.routes.PublicKeysListRoute; +import tech.pegasys.web3signer.core.routes.ReloadRoute; +import tech.pegasys.web3signer.core.routes.eth2.Eth2SignExtensionRoute; +import tech.pegasys.web3signer.core.routes.eth2.Eth2SignRoute; +import tech.pegasys.web3signer.core.routes.eth2.HighWatermarkRoute; +import tech.pegasys.web3signer.core.routes.eth2.KeyManagerApiRoute; import tech.pegasys.web3signer.keystorage.aws.AwsSecretsManagerProvider; import tech.pegasys.web3signer.keystorage.azure.AzureKeyVault; import tech.pegasys.web3signer.keystorage.common.MappedResults; import tech.pegasys.web3signer.keystorage.hashicorp.HashicorpConnectionFactory; import tech.pegasys.web3signer.signing.ArtifactSigner; import tech.pegasys.web3signer.signing.ArtifactSignerProvider; -import tech.pegasys.web3signer.signing.BlsArtifactSignature; import tech.pegasys.web3signer.signing.BlsArtifactSigner; -import tech.pegasys.web3signer.signing.FileValidatorManager; -import tech.pegasys.web3signer.signing.KeystoreFileManager; -import tech.pegasys.web3signer.signing.ValidatorManager; import tech.pegasys.web3signer.signing.bulkloading.BlsAwsBulkLoader; import tech.pegasys.web3signer.signing.bulkloading.BlsGcpBulkLoader; import tech.pegasys.web3signer.signing.bulkloading.BlsKeystoreBulkLoader; @@ -65,11 +55,9 @@ import tech.pegasys.web3signer.signing.config.metadata.yubihsm.YubiHsmOpaqueDataProvider; import tech.pegasys.web3signer.slashingprotection.DbHealthCheck; import tech.pegasys.web3signer.slashingprotection.DbPrunerRunner; -import tech.pegasys.web3signer.slashingprotection.DbValidatorManager; import tech.pegasys.web3signer.slashingprotection.SlashingProtectionContext; import tech.pegasys.web3signer.slashingprotection.SlashingProtectionContextFactory; import tech.pegasys.web3signer.slashingprotection.SlashingProtectionParameters; -import tech.pegasys.web3signer.slashingprotection.dao.ValidatorsDao; import java.util.ArrayList; import java.util.List; @@ -78,13 +66,9 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import com.fasterxml.jackson.databind.ObjectMapper; import io.vertx.core.Vertx; -import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonObject; import io.vertx.ext.healthchecks.Status; -import io.vertx.ext.web.Router; -import io.vertx.ext.web.impl.BlockingHandlerDecorator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.tuweni.bytes.Bytes; @@ -92,11 +76,6 @@ import org.hyperledger.besu.plugin.services.MetricsSystem; public class Eth2Runner extends Runner { - public static final String KEYSTORES_PATH = "/eth/v1/keystores"; - public static final String PUBLIC_KEYS_PATH = "/api/v1/eth2/publicKeys"; - public static final String SIGN_PATH = "/api/v1/eth2/sign/:identifier"; - public static final String SIGN_EXT_PATH = "/api/v1/eth2/ext/sign/:identifier"; - public static final String HIGH_WATERMARK_PATH = "/api/v1/eth2/highWatermark"; private static final Logger LOG = LogManager.getLogger(); private final Optional slashingProtectionContext; @@ -148,138 +127,46 @@ private Optional createSlashingProtection( @Override public void populateRouter(final Context context) { - registerEth2Routes( - context.getRouter(), - context.getArtifactSignerProvider(), - context.getErrorHandler(), - context.getMetricsSystem(), - slashingProtectionContext); - } - - private void registerEth2Routes( - final Router router, - final ArtifactSignerProvider blsSignerProvider, - final LogErrorHandler errorHandler, - final MetricsSystem metricsSystem, - final Optional slashingProtectionContext) { - final ObjectMapper objectMapper = SigningObjectMapperFactory.createObjectMapper(); - - addPublicKeysListHandler(router, blsSignerProvider, PUBLIC_KEYS_PATH, errorHandler); - - final SignerForIdentifier blsSigner = - new SignerForIdentifier<>(blsSignerProvider, this::formatBlsSignature, BLS); - router - .route(HttpMethod.POST, SIGN_PATH) - .handler( - new BlockingHandlerDecorator( - new Eth2SignForIdentifierHandler( - blsSigner, - new HttpApiMetrics(metricsSystem, BLS, blsSignerProvider), - new SlashingProtectionMetrics(metricsSystem), - slashingProtectionContext.map(SlashingProtectionContext::getSlashingProtection), - objectMapper, - eth2Spec), - false)) - .failureHandler(errorHandler); - - addReloadHandler(router, List.of(blsSignerProvider), errorHandler); - - slashingProtectionContext.ifPresent( - protectionContext -> - router - .route(HttpMethod.GET, HIGH_WATERMARK_PATH) - .handler(new HighWatermarkHandler(protectionContext.getSlashingProtection())) - .failureHandler(errorHandler)); - - addSigningExtHandler(router, errorHandler, blsSigner); - - if (isKeyManagerApiEnabled) { - router - .route(HttpMethod.GET, KEYSTORES_PATH) - .handler( - new BlockingHandlerDecorator( - new ListKeystoresHandler(blsSignerProvider, objectMapper), false)) - .failureHandler(errorHandler); - - final ValidatorManager validatorManager = - createValidatorManager(blsSignerProvider, objectMapper); - - router - .route(HttpMethod.POST, KEYSTORES_PATH) - .blockingHandler( - new ImportKeystoresHandler( - objectMapper, - baseConfig.getKeyConfigPath(), - slashingProtectionContext.map(SlashingProtectionContext::getSlashingProtection), - blsSignerProvider, - validatorManager), - false) - .failureHandler(errorHandler); - - router - .route(HttpMethod.DELETE, KEYSTORES_PATH) - .handler( - new BlockingHandlerDecorator( - new DeleteKeystoresHandler( - objectMapper, - slashingProtectionContext.map( - SlashingProtectionContext::getSlashingProtection), - blsSignerProvider, - validatorManager), - false)) - .failureHandler(errorHandler); + new PublicKeysListRoute(context, "eth2").register(); + new Eth2SignRoute(context, eth2Spec, slashingProtectionContext).register(); + new ReloadRoute(context).register(); + new HighWatermarkRoute(context, slashingProtectionContext).register(); + if (signingExtEnabled) { + new Eth2SignExtensionRoute(context).register(); } - } - - private ValidatorManager createValidatorManager( - final ArtifactSignerProvider artifactSignerProvider, final ObjectMapper objectMapper) { - final FileValidatorManager fileValidatorManager = - new FileValidatorManager( - artifactSignerProvider, - new KeystoreFileManager( - baseConfig.getKeyConfigPath(), - YamlMapperFactory.createYamlMapper(baseConfig.getKeyStoreConfigFileMaxSize())), - objectMapper); - if (slashingProtectionContext.isPresent()) { - final SlashingProtectionContext slashingProtectionContext = - this.slashingProtectionContext.get(); - return new DbValidatorManager( - fileValidatorManager, - slashingProtectionContext.getRegisteredValidators(), - slashingProtectionContext.getSlashingProtectionJdbi(), - new ValidatorsDao()); - } else { - return fileValidatorManager; + if (isKeyManagerApiEnabled) { + new KeyManagerApiRoute(context, baseConfig, slashingProtectionContext).register(); } } @Override - protected ArtifactSignerProvider createArtifactSignerProvider( + protected List createArtifactSignerProvider( final Vertx vertx, final MetricsSystem metricsSystem) { - return new DefaultArtifactSignerProvider( - () -> { - try (final AzureKeyVaultFactory azureKeyVaultFactory = new AzureKeyVaultFactory()) { - final List signers = new ArrayList<>(); - signers.addAll( - loadSignersFromKeyConfigFiles(vertx, azureKeyVaultFactory, metricsSystem) - .getValues()); - signers.addAll(bulkLoadSigners(azureKeyVaultFactory).getValues()); - - final List validators = - signers.stream() - .map(ArtifactSigner::getIdentifier) - .map(Bytes::fromHexString) - .collect(Collectors.toList()); - if (validators.isEmpty()) { - LOG.warn("No BLS keys loaded. Check that the key store has BLS key config files"); - } else { - slashingProtectionContext.ifPresent( - context -> context.getRegisteredValidators().registerValidators(validators)); - } - - return signers; - } - }); + return List.of( + new DefaultArtifactSignerProvider( + () -> { + try (final AzureKeyVaultFactory azureKeyVaultFactory = new AzureKeyVaultFactory()) { + final List signers = new ArrayList<>(); + signers.addAll( + loadSignersFromKeyConfigFiles(vertx, azureKeyVaultFactory, metricsSystem) + .getValues()); + signers.addAll(bulkLoadSigners(azureKeyVaultFactory).getValues()); + + final List validators = + signers.stream() + .map(ArtifactSigner::getIdentifier) + .map(Bytes::fromHexString) + .collect(Collectors.toList()); + if (validators.isEmpty()) { + LOG.warn("No BLS keys loaded. Check that the key store has BLS key config files"); + } else { + slashingProtectionContext.ifPresent( + context -> context.getRegisteredValidators().registerValidators(validators)); + } + + return signers; + } + })); } private MappedResults loadSignersFromKeyConfigFiles( @@ -401,35 +288,6 @@ private void registerSignerLoadingHealthCheck( }); } - private void addSigningExtHandler( - final Router router, - final LogErrorHandler errorHandler, - final SignerForIdentifier signer) { - if (!signingExtEnabled) { - return; - } - - router - .route(HttpMethod.POST, SIGN_EXT_PATH) - .blockingHandler(new SigningExtensionHandler(signer), false) - .failureHandler(errorHandler) - .failureHandler( - ctx -> { - final int statusCode = ctx.statusCode(); - if (statusCode == 400) { - ctx.response() - .setStatusCode(statusCode) - .end(new JsonObject().put("error", "Bad Request").encode()); - } else if (statusCode == 404) { - ctx.response() - .setStatusCode(statusCode) - .end(new JsonObject().put("error", "Identifier not found.").encode()); - } else { - ctx.next(); // go to global failure handler - } - }); - } - @Override public void run() { super.run(); @@ -490,8 +348,4 @@ final MappedResults loadAzureSigners( }, azureKeyVaultParameters.getTags()); } - - private String formatBlsSignature(final BlsArtifactSignature signature) { - return signature.getSignatureData().toString(); - } } 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 8774ed906..4fdb1912e 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/Runner.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/Runner.java @@ -21,11 +21,9 @@ import tech.pegasys.web3signer.core.config.MetricsPushOptions; import tech.pegasys.web3signer.core.config.TlsOptions; import tech.pegasys.web3signer.core.metrics.vertx.VertxMetricsAdapterFactory; +import tech.pegasys.web3signer.core.routes.SwaggerUIRoute; import tech.pegasys.web3signer.core.service.http.HostAllowListHandler; -import tech.pegasys.web3signer.core.service.http.SwaggerUIRoute; import tech.pegasys.web3signer.core.service.http.handlers.LogErrorHandler; -import tech.pegasys.web3signer.core.service.http.handlers.PublicKeysListHandler; -import tech.pegasys.web3signer.core.service.http.handlers.ReloadHandler; import tech.pegasys.web3signer.core.service.http.handlers.UpcheckHandler; import tech.pegasys.web3signer.core.util.FileUtil; import tech.pegasys.web3signer.signing.ArtifactSignerProvider; @@ -65,7 +63,6 @@ import io.vertx.ext.web.handler.CorsHandler; import io.vertx.ext.web.handler.LoggerFormat; import io.vertx.ext.web.handler.LoggerHandler; -import io.vertx.ext.web.impl.BlockingHandlerDecorator; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.config.Configurator; @@ -81,7 +78,6 @@ public abstract class Runner implements Runnable, AutoCloseable { 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"; private static final Logger LOG = LogManager.getLogger(); @@ -115,20 +111,25 @@ public void run() { final LogErrorHandler errorHandler = new LogErrorHandler(); healthCheckHandler = HealthCheckHandler.create(vertx); - final ArtifactSignerProvider artifactSignerProvider = - createArtifactSignerProvider(vertx, metricsSystem); + final List artifactSignerProviders = + Optional.ofNullable(createArtifactSignerProvider(vertx, metricsSystem)).orElse(List.of()); try { createVersionMetric(metricsSystem); metricsService = MetricsService.create(vertx, metricsConfiguration, metricsSystem); metricsService.ifPresent(MetricsService::start); - try { - artifactSignerProvider.load().get(); // wait for signers to get loaded ... - } catch (final InterruptedException | ExecutionException e) { - LOG.error("Error loading signers", e); - registerHealthCheckProcedure( - KEYS_CHECK_UNEXPECTED, promise -> promise.complete(Status.KO())); - } + + // load the artifact signer providers in order ... + artifactSignerProviders.forEach( + provider -> { + try { + provider.load().get(); // wait for signers to get loaded ... + } catch (final InterruptedException | ExecutionException e) { + LOG.error("Error loading signers", e); + registerHealthCheckProcedure( + KEYS_CHECK_UNEXPECTED, promise -> promise.complete(Status.KO())); + } + }); // register access log handler first if (baseConfig.isAccessLogsEnabled()) { @@ -162,7 +163,7 @@ public void run() { registerHealthCheckProcedure(DEFAULT_CHECK, promise -> promise.complete(Status.OK())); final Context context = - new Context(router, metricsSystem, errorHandler, vertx, artifactSignerProvider); + new Context(router, metricsSystem, errorHandler, vertx, artifactSignerProviders); populateRouter(context); if (baseConfig.isSwaggerUIEnabled()) { @@ -182,9 +183,7 @@ public void run() { closeables.add(() -> shutdownVertx(vertx)); } catch (final Throwable e) { - if (artifactSignerProvider != null) { - artifactSignerProvider.close(); - } + artifactSignerProviders.forEach(ArtifactSignerProvider::close); shutdownVertx(vertx); metricsService.ifPresent(MetricsService::stop); LOG.error("Failed to initialise application", e); @@ -237,35 +236,11 @@ private VertxOptions createVertxOptions() { .setMetricsOptions(new MetricsOptions().setEnabled(true)); } - protected abstract ArtifactSignerProvider createArtifactSignerProvider( + protected abstract List createArtifactSignerProvider( final Vertx vertx, final MetricsSystem metricsSystem); protected abstract void populateRouter(final Context context); - protected void addPublicKeysListHandler( - final Router router, - final ArtifactSignerProvider artifactSignerProvider, - final String path, - final LogErrorHandler errorHandler) { - router - .route(HttpMethod.GET, path) - .produces(JSON) - .handler( - new BlockingHandlerDecorator(new PublicKeysListHandler(artifactSignerProvider), false)) - .failureHandler(errorHandler); - } - - protected void addReloadHandler( - final Router router, - final List orderedArtifactSignerProviders, - final LogErrorHandler errorHandler) { - router - .route(HttpMethod.POST, RELOAD_PATH) - .produces(JSON) - .handler(new ReloadHandler(orderedArtifactSignerProviders)) - .failureHandler(errorHandler); - } - private void registerUpcheckRoute(final Router router, final LogErrorHandler errorHandler) { router .route(HttpMethod.GET, UPCHECK_PATH) diff --git a/core/src/main/java/tech/pegasys/web3signer/core/WebClientOptionsFactory.java b/core/src/main/java/tech/pegasys/web3signer/core/WebClientOptionsFactory.java index 6f8c9db8c..a2b6de037 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/WebClientOptionsFactory.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/WebClientOptionsFactory.java @@ -27,7 +27,7 @@ import io.vertx.core.net.ProxyOptions; import io.vertx.ext.web.client.WebClientOptions; -class WebClientOptionsFactory { +public class WebClientOptionsFactory { public WebClientOptions createWebClientOptions(final Eth1Config eth1Config) { final WebClientOptions clientOptions = diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/PublicKeysListRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/PublicKeysListRoute.java new file mode 100644 index 000000000..60d72b0e0 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/PublicKeysListRoute.java @@ -0,0 +1,51 @@ +/* + * Copyright 2024 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.routes; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.service.http.handlers.PublicKeysListHandler; + +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.impl.BlockingHandlerDecorator; + +public class PublicKeysListRoute implements Web3SignerRoute { + + private static final String ETH1_PATH = "/api/v1/eth1/publicKeys"; + private static final String ETH2_PATH = "/api/v1/eth2/publicKeys"; + + private final Context context; + private final String path; + + public PublicKeysListRoute(final Context context, final String mode) { + this.context = context; + this.path = + switch (mode) { + case "eth1" -> ETH1_PATH; + case "eth2" -> ETH2_PATH; + default -> + throw new IllegalArgumentException("Unsupported mode for Public Key Route: " + mode); + }; + } + + @Override + public void register() { + context + .getRouter() + .route(HttpMethod.GET, path) + .produces(JSON_HEADER) + .handler( + new BlockingHandlerDecorator( + new PublicKeysListHandler(context.getArtifactSignerProviders()), false)) + .failureHandler(context.getErrorHandler()); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/ReloadRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/ReloadRoute.java new file mode 100644 index 000000000..cda187a2b --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/ReloadRoute.java @@ -0,0 +1,37 @@ +/* + * Copyright 2024 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.routes; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.service.http.handlers.ReloadHandler; + +import io.vertx.core.http.HttpMethod; + +public class ReloadRoute implements Web3SignerRoute { + private static final String RELOAD_PATH = "/reload"; + private final Context context; + + public ReloadRoute(final Context context) { + this.context = context; + } + + @Override + public void register() { + context + .getRouter() + .route(HttpMethod.POST, RELOAD_PATH) + .produces(JSON_HEADER) + .handler(new ReloadHandler(context.getArtifactSignerProviders())) + .failureHandler(context.getErrorHandler()); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/SwaggerUIRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/SwaggerUIRoute.java similarity index 87% rename from core/src/main/java/tech/pegasys/web3signer/core/service/http/SwaggerUIRoute.java rename to core/src/main/java/tech/pegasys/web3signer/core/routes/SwaggerUIRoute.java index aa1f547b8..13823fcaf 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/http/SwaggerUIRoute.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/SwaggerUIRoute.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.web3signer.core.routes; import static java.nio.charset.StandardCharsets.UTF_8; @@ -30,7 +30,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class SwaggerUIRoute { +public class SwaggerUIRoute implements Web3SignerRoute { private static final Logger LOG = LogManager.getLogger(); private static final String CONTENT_TYPE_TEXT_HTML = "text/html; charset=utf-8"; private static final String SWAGGER_ENDPOINT = "/swagger-ui"; @@ -40,13 +40,19 @@ public SwaggerUIRoute(final Router router) { this.router = router; } - public void register() throws IOException { + @Override + public void register() { LOG.info("Registering /swagger-ui routes ..."); - final OpenApiSpecsExtractor openApiSpecsExtractor = - new OpenApiSpecsExtractor.OpenApiSpecsExtractorBuilder() - .withConvertRelativeRefToAbsoluteRef(false) - .withForceDeleteOnJvmExit(true) - .build(); + final OpenApiSpecsExtractor openApiSpecsExtractor; + try { + openApiSpecsExtractor = + new OpenApiSpecsExtractor.OpenApiSpecsExtractorBuilder() + .withConvertRelativeRefToAbsoluteRef(false) + .withForceDeleteOnJvmExit(true) + .build(); + } catch (final IOException e) { + throw new UncheckedIOException(e); + } final Map swaggerUIWebRoot; swaggerUIWebRoot = loadSwaggerUIStaticContent(openApiSpecsExtractor); diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/Web3SignerRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/Web3SignerRoute.java new file mode 100644 index 000000000..ec0cc1fde --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/Web3SignerRoute.java @@ -0,0 +1,21 @@ +/* + * Copyright 2024 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.routes; + +import io.netty.handler.codec.http.HttpHeaderValues; + +public interface Web3SignerRoute { + String JSON_HEADER = HttpHeaderValues.APPLICATION_JSON.toString(); + + void register(); +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/eth1/Eth1SignRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth1/Eth1SignRoute.java new file mode 100644 index 000000000..cdd1db7f1 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth1/Eth1SignRoute.java @@ -0,0 +1,71 @@ +/* + * Copyright 2024 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.routes.eth1; + +import static tech.pegasys.web3signer.signing.KeyType.SECP256K1; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.routes.Web3SignerRoute; +import tech.pegasys.web3signer.core.service.http.handlers.signing.Eth1SignForIdentifierHandler; +import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier; +import tech.pegasys.web3signer.core.service.http.metrics.HttpApiMetrics; +import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import tech.pegasys.web3signer.signing.SecpArtifactSignature; +import tech.pegasys.web3signer.signing.config.DefaultArtifactSignerProvider; + +import java.util.Optional; + +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.impl.BlockingHandlerDecorator; + +public class Eth1SignRoute implements Web3SignerRoute { + private static final String SIGN_PATH = "/api/v1/eth1/sign/:identifier"; + + private final Context context; + private final ArtifactSignerProvider signerProvider; + private final SignerForIdentifier secpSigner; + + public Eth1SignRoute(final Context context) { + this.context = context; + + // we need signerProvider which is an instance of DefaultArtifactSignerProvider + final Optional first = + context.getArtifactSignerProviders().stream() + .filter(provider -> provider instanceof DefaultArtifactSignerProvider) + .findFirst(); + + if (first.isPresent()) { + signerProvider = first.get(); + secpSigner = + new SignerForIdentifier<>( + signerProvider, sig -> SecpArtifactSignature.toBytes(sig).toHexString(), SECP256K1); + } else { + throw new IllegalStateException( + "No DefaultArtifactSignerProvider found in Context for eth1 mode"); + } + } + + @Override + public void register() { + context + .getRouter() + .route(HttpMethod.POST, SIGN_PATH) + .handler( + new BlockingHandlerDecorator( + new Eth1SignForIdentifierHandler( + secpSigner, + new HttpApiMetrics(context.getMetricsSystem(), SECP256K1, signerProvider)), + false)) + .failureHandler(context.getErrorHandler()); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/eth1/JsonRpcRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth1/JsonRpcRoute.java new file mode 100644 index 000000000..ffefa21b3 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth1/JsonRpcRoute.java @@ -0,0 +1,163 @@ +/* + * Copyright 2024 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.routes.eth1; + +import static tech.pegasys.web3signer.signing.KeyType.SECP256K1; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.Runner; +import tech.pegasys.web3signer.core.WebClientOptionsFactory; +import tech.pegasys.web3signer.core.config.Eth1Config; +import tech.pegasys.web3signer.core.routes.Web3SignerRoute; +import tech.pegasys.web3signer.core.service.DownstreamPathCalculator; +import tech.pegasys.web3signer.core.service.VertxRequestTransmitter; +import tech.pegasys.web3signer.core.service.VertxRequestTransmitterFactory; +import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier; +import tech.pegasys.web3signer.core.service.jsonrpc.Eth1JsonDecoderFactory; +import tech.pegasys.web3signer.core.service.jsonrpc.JsonDecoder; +import tech.pegasys.web3signer.core.service.jsonrpc.handlers.Eth1AccountsHandler; +import tech.pegasys.web3signer.core.service.jsonrpc.handlers.HttpResponseFactory; +import tech.pegasys.web3signer.core.service.jsonrpc.handlers.JsonRpcErrorHandler; +import tech.pegasys.web3signer.core.service.jsonrpc.handlers.JsonRpcHandler; +import tech.pegasys.web3signer.core.service.jsonrpc.handlers.PassThroughHandler; +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; +import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import tech.pegasys.web3signer.signing.SecpArtifactSignature; +import tech.pegasys.web3signer.signing.config.SecpArtifactSignerProviderAdapter; + +import java.util.Optional; + +import io.vertx.core.Vertx; +import io.vertx.core.http.HttpClient; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.client.WebClientOptions; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.ResponseContentTypeHandler; + +public class JsonRpcRoute implements Web3SignerRoute { + private static final String ROOT_PATH = "/"; + private static final JsonDecoder JSON_DECODER = Eth1JsonDecoderFactory.create(); + private static final HttpResponseFactory HTTP_RESPONSE_FACTORY = new HttpResponseFactory(); + + private final Context context; + private final VertxRequestTransmitterFactory transmitterFactory; + private final RequestMapper requestMapper; + + public JsonRpcRoute(final Context context, final Eth1Config eth1Config) { + this.context = context; + + // we need signerProvider which is an instance of SecpArtifactSignerProviderAdapter + final Optional first = + context.getArtifactSignerProviders().stream() + .filter(provider -> provider instanceof SecpArtifactSignerProviderAdapter) + .findFirst(); + final ArtifactSignerProvider signerProvider; + if (first.isPresent()) { + signerProvider = first.get(); + } else { + throw new IllegalStateException( + "No SecpArtifactSignerProviderAdapter found in Context for eth1 mode"); + } + // use same instance of downstreamHttpClient and path calculator for all requests + final HttpClient downstreamHttpClient = + createDownstreamHttpClient(eth1Config, context.getVertx()); + final DownstreamPathCalculator downstreamPathCalculator = + new DownstreamPathCalculator(eth1Config.getDownstreamHttpPath()); + + transmitterFactory = + responseBodyHandler -> + new VertxRequestTransmitter( + context.getVertx(), + downstreamHttpClient, + eth1Config.getDownstreamHttpRequestTimeout(), + downstreamPathCalculator, + responseBodyHandler); + + requestMapper = + createRequestMapper(transmitterFactory, signerProvider, eth1Config.getChainId().id()); + } + + @Override + public void register() { + context + .getRouter() + .route(HttpMethod.POST, ROOT_PATH) + .produces(Runner.JSON) + .handler(ResponseContentTypeHandler.create()) + .handler(BodyHandler.create()) + .failureHandler(new JsonRpcErrorHandler(new HttpResponseFactory())) + .blockingHandler( + new JsonRpcHandler(HTTP_RESPONSE_FACTORY, requestMapper, JSON_DECODER), false); + + // proxy everything else to Besu using passThrough handler + context + .getRouter() + .route() + .handler(BodyHandler.create()) + .handler(new PassThroughHandler(transmitterFactory, JSON_DECODER)); + } + + private static HttpClient createDownstreamHttpClient( + final Eth1Config eth1Config, final Vertx vertx) { + final WebClientOptions webClientOptions = + new WebClientOptionsFactory().createWebClientOptions(eth1Config); + return vertx.createHttpClient(webClientOptions); + } + + private static RequestMapper createRequestMapper( + final VertxRequestTransmitterFactory transmitterFactory, + final ArtifactSignerProvider signerProviderMappedToEth1Address, + final long chainId) { + final PassThroughHandler defaultHandler = + new PassThroughHandler(transmitterFactory, JSON_DECODER); + final SignerForIdentifier secpSigner = + new SignerForIdentifier<>( + signerProviderMappedToEth1Address, + sig -> SecpArtifactSignature.toBytes(sig).toHexString(), + SECP256K1); + final TransactionFactory transactionFactory = + new TransactionFactory(chainId, JSON_DECODER, transmitterFactory); + final SendTransactionHandler sendTransactionHandler = + new SendTransactionHandler(chainId, transactionFactory, transmitterFactory, secpSigner); + + final RequestMapper requestMapper = new RequestMapper(defaultHandler); + requestMapper.addHandler( + "eth_accounts", + new InternalResponseHandler<>( + HTTP_RESPONSE_FACTORY, + new Eth1AccountsHandler(signerProviderMappedToEth1Address::availableIdentifiers))); + requestMapper.addHandler( + "eth_sign", + new InternalResponseHandler<>( + HTTP_RESPONSE_FACTORY, new EthSignResultProvider(secpSigner))); + requestMapper.addHandler( + "eth_signTypedData", + new InternalResponseHandler<>( + HTTP_RESPONSE_FACTORY, new EthSignTypedDataResultProvider(secpSigner))); + requestMapper.addHandler( + "eth_signTransaction", + new InternalResponseHandler<>( + HTTP_RESPONSE_FACTORY, + new EthSignTransactionResultProvider(chainId, secpSigner, JSON_DECODER))); + requestMapper.addHandler("eth_sendTransaction", sendTransactionHandler); + requestMapper.addHandler("eea_sendTransaction", sendTransactionHandler); + + return requestMapper; + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/Eth2SignExtensionRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/Eth2SignExtensionRoute.java new file mode 100644 index 000000000..c5caac682 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/Eth2SignExtensionRoute.java @@ -0,0 +1,69 @@ +/* + * Copyright 2024 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.routes.eth2; + +import static tech.pegasys.web3signer.signing.KeyType.BLS; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.routes.Web3SignerRoute; +import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier; +import tech.pegasys.web3signer.core.service.http.handlers.signing.SigningExtensionHandler; +import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import tech.pegasys.web3signer.signing.BlsArtifactSignature; + +import io.vertx.core.http.HttpMethod; +import io.vertx.core.json.JsonObject; + +public class Eth2SignExtensionRoute implements Web3SignerRoute { + public static final String SIGN_EXT_PATH = "/api/v1/eth2/ext/sign/:identifier"; + + private final Context context; + private final SignerForIdentifier blsSigner; + + public Eth2SignExtensionRoute(final Context context) { + this.context = context; + + // there should be only one ArtifactSignerProvider in eth2 mode at the moment which is of BLS + // types. + final ArtifactSignerProvider artifactSignerProvider = + context.getArtifactSignerProviders().stream().findFirst().orElseThrow(); + + blsSigner = + new SignerForIdentifier<>( + artifactSignerProvider, sig -> sig.getSignatureData().toString(), BLS); + } + + @Override + public void register() { + context + .getRouter() + .route(HttpMethod.POST, SIGN_EXT_PATH) + .blockingHandler(new SigningExtensionHandler(blsSigner), false) + .failureHandler(context.getErrorHandler()) + .failureHandler( + ctx -> { + final int statusCode = ctx.statusCode(); + if (statusCode == 400) { + ctx.response() + .setStatusCode(statusCode) + .end(new JsonObject().put("error", "Bad Request").encode()); + } else if (statusCode == 404) { + ctx.response() + .setStatusCode(statusCode) + .end(new JsonObject().put("error", "Identifier not found.").encode()); + } else { + ctx.next(); // go to global failure handler + } + }); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/Eth2SignRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/Eth2SignRoute.java new file mode 100644 index 000000000..c54e42da6 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/Eth2SignRoute.java @@ -0,0 +1,84 @@ +/* + * Copyright 2024 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.routes.eth2; + +import static tech.pegasys.web3signer.signing.KeyType.BLS; + +import tech.pegasys.teku.spec.Spec; +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.metrics.SlashingProtectionMetrics; +import tech.pegasys.web3signer.core.routes.Web3SignerRoute; +import tech.pegasys.web3signer.core.service.http.SigningObjectMapperFactory; +import tech.pegasys.web3signer.core.service.http.handlers.signing.SignerForIdentifier; +import tech.pegasys.web3signer.core.service.http.handlers.signing.eth2.Eth2SignForIdentifierHandler; +import tech.pegasys.web3signer.core.service.http.metrics.HttpApiMetrics; +import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import tech.pegasys.web3signer.signing.BlsArtifactSignature; +import tech.pegasys.web3signer.slashingprotection.SlashingProtection; +import tech.pegasys.web3signer.slashingprotection.SlashingProtectionContext; + +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.impl.BlockingHandlerDecorator; + +public class Eth2SignRoute implements Web3SignerRoute { + private static final String SIGN_PATH = "/api/v1/eth2/sign/:identifier"; + private final Context context; + private final SignerForIdentifier blsSigner; + private final ObjectMapper objectMapper = SigningObjectMapperFactory.createObjectMapper(); + private final Spec eth2Spec; + private final Optional slashingProtection; + + public Eth2SignRoute( + final Context context, + final Spec eth2Spec, + final Optional slashingProtectionContext) { + this.context = context; + this.eth2Spec = eth2Spec; + slashingProtection = + slashingProtectionContext.map(SlashingProtectionContext::getSlashingProtection); + // there should be only one ArtifactSignerProvider in eth2 mode at the moment which is of BLS + // types. + final ArtifactSignerProvider artifactSignerProvider = + context.getArtifactSignerProviders().stream().findFirst().orElseThrow(); + + blsSigner = + new SignerForIdentifier<>( + artifactSignerProvider, sig -> sig.getSignatureData().toString(), BLS); + } + + @Override + public void register() { + // there should be only one ArtifactSignerProvider in eth2 mode at the moment which is of BLS + // types. + final ArtifactSignerProvider artifactSignerProvider = + context.getArtifactSignerProviders().stream().findFirst().orElseThrow(); + + context + .getRouter() + .route(HttpMethod.POST, SIGN_PATH) + .handler( + new BlockingHandlerDecorator( + new Eth2SignForIdentifierHandler( + blsSigner, + new HttpApiMetrics(context.getMetricsSystem(), BLS, artifactSignerProvider), + new SlashingProtectionMetrics(context.getMetricsSystem()), + slashingProtection, + objectMapper, + eth2Spec), + false)) + .failureHandler(context.getErrorHandler()); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/HighWatermarkRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/HighWatermarkRoute.java new file mode 100644 index 000000000..93b5e723d --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/HighWatermarkRoute.java @@ -0,0 +1,45 @@ +/* + * Copyright 2024 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.routes.eth2; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.routes.Web3SignerRoute; +import tech.pegasys.web3signer.core.service.http.handlers.HighWatermarkHandler; +import tech.pegasys.web3signer.slashingprotection.SlashingProtectionContext; + +import java.util.Optional; + +import io.vertx.core.http.HttpMethod; + +public class HighWatermarkRoute implements Web3SignerRoute { + public static final String HIGH_WATERMARK_PATH = "/api/v1/eth2/highWatermark"; + private final Context context; + private final Optional slashingProtectionContext; + + public HighWatermarkRoute( + final Context context, final Optional slashingProtectionContext) { + this.context = context; + this.slashingProtectionContext = slashingProtectionContext; + } + + @Override + public void register() { + slashingProtectionContext.ifPresent( + protectionContext -> + context + .getRouter() + .route(HttpMethod.GET, HIGH_WATERMARK_PATH) + .handler(new HighWatermarkHandler(protectionContext.getSlashingProtection())) + .failureHandler(context.getErrorHandler())); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/KeyManagerApiRoute.java b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/KeyManagerApiRoute.java new file mode 100644 index 000000000..5e2410125 --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/routes/eth2/KeyManagerApiRoute.java @@ -0,0 +1,131 @@ +/* + * Copyright 2024 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.routes.eth2; + +import tech.pegasys.web3signer.core.Context; +import tech.pegasys.web3signer.core.config.BaseConfig; +import tech.pegasys.web3signer.core.routes.Web3SignerRoute; +import tech.pegasys.web3signer.core.service.http.SigningObjectMapperFactory; +import tech.pegasys.web3signer.core.service.http.handlers.keymanager.delete.DeleteKeystoresHandler; +import tech.pegasys.web3signer.core.service.http.handlers.keymanager.imports.ImportKeystoresHandler; +import tech.pegasys.web3signer.core.service.http.handlers.keymanager.list.ListKeystoresHandler; +import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import tech.pegasys.web3signer.signing.FileValidatorManager; +import tech.pegasys.web3signer.signing.KeystoreFileManager; +import tech.pegasys.web3signer.signing.ValidatorManager; +import tech.pegasys.web3signer.signing.config.metadata.parser.YamlMapperFactory; +import tech.pegasys.web3signer.slashingprotection.DbValidatorManager; +import tech.pegasys.web3signer.slashingprotection.SlashingProtection; +import tech.pegasys.web3signer.slashingprotection.SlashingProtectionContext; +import tech.pegasys.web3signer.slashingprotection.dao.ValidatorsDao; + +import java.util.Optional; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.vertx.core.http.HttpMethod; +import io.vertx.ext.web.impl.BlockingHandlerDecorator; + +public class KeyManagerApiRoute implements Web3SignerRoute { + public static final String KEYSTORES_PATH = "/eth/v1/keystores"; + private final Context context; + private final ArtifactSignerProvider blsSignerProvider; + private final Optional slashingProtectionContext; + private final Optional slashingProtection; + private final ObjectMapper objectMapper = SigningObjectMapperFactory.createObjectMapper(); + private final BaseConfig baseConfig; + + public KeyManagerApiRoute( + final Context context, + final BaseConfig baseConfig, + final Optional slashingProtectionContext) { + this.context = context; + this.baseConfig = baseConfig; + this.slashingProtectionContext = slashingProtectionContext; + + slashingProtection = + slashingProtectionContext.map(SlashingProtectionContext::getSlashingProtection); + // there should be only one ArtifactSignerProvider in eth2 mode at the moment which is of BLS + // types. + blsSignerProvider = context.getArtifactSignerProviders().stream().findFirst().orElseThrow(); + } + + @Override + public void register() { + registerGet(); + + // TODO: should there be separate instance for POST and DELETE? + final ValidatorManager validatorManager = createValidatorManager(); + + registerPost(validatorManager); + + registerDelete(validatorManager); + } + + private void registerGet() { + context + .getRouter() + .route(HttpMethod.GET, KEYSTORES_PATH) + .handler( + new BlockingHandlerDecorator( + new ListKeystoresHandler(blsSignerProvider, objectMapper), false)) + .failureHandler(context.getErrorHandler()); + } + + private void registerPost(ValidatorManager validatorManager) { + context + .getRouter() + .route(HttpMethod.POST, KEYSTORES_PATH) + .blockingHandler( + new ImportKeystoresHandler( + objectMapper, + baseConfig.getKeyConfigPath(), + slashingProtection, + blsSignerProvider, + validatorManager), + false) + .failureHandler(context.getErrorHandler()); + } + + private void registerDelete(ValidatorManager validatorManager) { + context + .getRouter() + .route(HttpMethod.DELETE, KEYSTORES_PATH) + .handler( + new BlockingHandlerDecorator( + new DeleteKeystoresHandler( + objectMapper, slashingProtection, blsSignerProvider, validatorManager), + false)) + .failureHandler(context.getErrorHandler()); + } + + private ValidatorManager createValidatorManager() { + final FileValidatorManager fileValidatorManager = + new FileValidatorManager( + blsSignerProvider, + new KeystoreFileManager( + baseConfig.getKeyConfigPath(), + YamlMapperFactory.createYamlMapper(baseConfig.getKeyStoreConfigFileMaxSize())), + objectMapper); + + return slashingProtectionContext + .map( + ctx -> + (ValidatorManager) + new DbValidatorManager( + fileValidatorManager, + ctx.getRegisteredValidators(), + ctx.getSlashingProtectionJdbi(), + new ValidatorsDao())) + .orElse(fileValidatorManager); + } +} diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/PublicKeysListHandler.java b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/PublicKeysListHandler.java index 63421b66a..d373b2916 100644 --- a/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/PublicKeysListHandler.java +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/http/handlers/PublicKeysListHandler.java @@ -16,25 +16,32 @@ import static tech.pegasys.web3signer.core.service.http.handlers.ContentTypes.JSON_UTF_8; import tech.pegasys.web3signer.signing.ArtifactSignerProvider; +import tech.pegasys.web3signer.signing.config.DefaultArtifactSignerProvider; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import io.vertx.core.Handler; import io.vertx.core.json.JsonArray; import io.vertx.ext.web.RoutingContext; public class PublicKeysListHandler implements Handler { - private final ArtifactSignerProvider artifactSignerProvider; + private final List artifactSignerProviders; - public PublicKeysListHandler(final ArtifactSignerProvider artifactSignerProvider) { - this.artifactSignerProvider = artifactSignerProvider; + public PublicKeysListHandler(final List artifactSignerProviders) { + this.artifactSignerProviders = artifactSignerProviders; } @Override public void handle(final RoutingContext context) { + // at the moment, we only support DefaultArtifactSignerProvider subclass that contains primary + // key as identifiers final List availableIdentifiers = - new ArrayList<>(artifactSignerProvider.availableIdentifiers()); + artifactSignerProviders.stream() + .filter(provider -> provider instanceof DefaultArtifactSignerProvider) + .flatMap(provider -> provider.availableIdentifiers().stream()) + .collect(Collectors.toList()); + final String jsonEncodedKeys = new JsonArray(availableIdentifiers).encode(); context.response().putHeader(CONTENT_TYPE, JSON_UTF_8).end(jsonEncodedKeys); } diff --git a/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/Eth1JsonDecoderFactory.java b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/Eth1JsonDecoderFactory.java new file mode 100644 index 000000000..3e2db505a --- /dev/null +++ b/core/src/main/java/tech/pegasys/web3signer/core/service/jsonrpc/Eth1JsonDecoderFactory.java @@ -0,0 +1,35 @@ +/* + * Copyright 2020 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; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class Eth1JsonDecoderFactory { + private final JsonDecoder jsonDecoder; + + private static final Eth1JsonDecoderFactory FACTORY = new Eth1JsonDecoderFactory(); + + private Eth1JsonDecoderFactory() { + // Force Transaction Deserialization to fail if missing expected properties + final ObjectMapper jsonObjectMapper = new ObjectMapper(); + jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_NULL_CREATOR_PROPERTIES, true); + jsonObjectMapper.configure(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES, true); + + jsonDecoder = new JsonDecoder(jsonObjectMapper); + } + + public static JsonDecoder create() { + return FACTORY.jsonDecoder; + } +} diff --git a/core/src/test/java/tech/pegasys/web3signer/core/service/http/SwaggerUIRouteTest.java b/core/src/test/java/tech/pegasys/web3signer/core/routes/SwaggerUIRouteTest.java similarity index 97% rename from core/src/test/java/tech/pegasys/web3signer/core/service/http/SwaggerUIRouteTest.java rename to core/src/test/java/tech/pegasys/web3signer/core/routes/SwaggerUIRouteTest.java index 1165a03f1..7bc23807e 100644 --- a/core/src/test/java/tech/pegasys/web3signer/core/service/http/SwaggerUIRouteTest.java +++ b/core/src/test/java/tech/pegasys/web3signer/core/routes/SwaggerUIRouteTest.java @@ -10,7 +10,7 @@ * 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; +package tech.pegasys.web3signer.core.routes; import static org.assertj.core.api.Assertions.assertThat; diff --git a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EeaSendTransactionJsonParametersTest.java b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EeaSendTransactionJsonParametersTest.java index e54b6e218..cb456ef27 100644 --- a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EeaSendTransactionJsonParametersTest.java +++ b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EeaSendTransactionJsonParametersTest.java @@ -16,7 +16,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import tech.pegasys.web3signer.core.Eth1Runner; import tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.transaction.TransactionFactory; import java.math.BigInteger; @@ -35,7 +34,7 @@ public class EeaSendTransactionJsonParametersTest { @BeforeEach public void setup() { // NOTE: the factory has been configured as per its use in the application. - factory = new TransactionFactory(1337L, Eth1Runner.createJsonDecoder(), null); + factory = new TransactionFactory(1337L, Eth1JsonDecoderFactory.create(), null); } @Test diff --git a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java index 6e922fb6c..ee73fd9e8 100644 --- a/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java +++ b/core/src/test/java/tech/pegasys/web3signer/core/service/jsonrpc/EthSendTransactionJsonParametersTest.java @@ -14,7 +14,6 @@ import static org.assertj.core.api.Assertions.assertThat; -import tech.pegasys.web3signer.core.Eth1Runner; import tech.pegasys.web3signer.core.service.jsonrpc.handlers.sendtransaction.transaction.TransactionFactory; import java.math.BigInteger; @@ -31,7 +30,7 @@ public class EthSendTransactionJsonParametersTest { @BeforeEach public void setup() { // NOTE: the factory has been configured as per its use in the application. - factory = new TransactionFactory(1337L, Eth1Runner.createJsonDecoder(), null); + factory = new TransactionFactory(1337L, Eth1JsonDecoderFactory.create(), null); } private Optional getStringAsOptionalBigInteger(