Skip to content

Commit

Permalink
Respond to eth_accounts RPC
Browse files Browse the repository at this point in the history
  • Loading branch information
rain-on authored Apr 5, 2019
1 parent a48ad4c commit e83a81c
Show file tree
Hide file tree
Showing 13 changed files with 404 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2019 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.ethfirewall.jsonrpcproxy;

import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;

import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcSuccessResponse;

import java.util.Map;

import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.json.Json;
import org.junit.Test;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.response.EthAccounts;

public class EthAccountsIntegrationTest extends IntegrationTestBase {

@Test
public void ethAccountsRequestFromWeb3jRespondsWithNodesAddress() {

final Request<?, EthAccounts> requestBody = jsonRpc().ethAccounts();
final Map<String, String> expectedHeaders =
singletonMap("Content", HttpHeaderValues.APPLICATION_JSON.toString());

final JsonRpcSuccessResponse responseBody =
new JsonRpcSuccessResponse(requestBody.getId(), singletonList(unlockedAccount));

sendRequestThenVerifyResponse(
request.ethFirewall(Json.encode(requestBody)),
response.ethFirewall(expectedHeaders, Json.encode(responseBody)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ public class IntegrationTestBase {
protected final EthRequestFactory request = new EthRequestFactory();
protected final EthResponseFactory response = new EthResponseFactory();

protected static String unlockedAccount;

@BeforeClass
public static void setupEthFirewall() throws IOException, CipherException {
setupEthFirewall(DEFAULT_CHAIN_ID);
Expand Down Expand Up @@ -109,6 +111,8 @@ protected static void setupEthFirewall(final long chainId) throws IOException, C
serverSocket.getLocalPort(),
clientAndServer.getLocalPort());
serverSocket.close();

unlockedAccount = transactionSigner.getAddress();
}

protected static void resetEthFirewall() throws IOException, CipherException {
Expand Down
33 changes: 24 additions & 9 deletions ethfirewall/src/main/java/tech/pegasys/ethfirewall/Runner.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
*/
package tech.pegasys.ethfirewall;

import tech.pegasys.ethfirewall.jsonrpcproxy.EthAccountsBodyProvider;
import tech.pegasys.ethfirewall.jsonrpcproxy.HttpResponseFactory;
import tech.pegasys.ethfirewall.jsonrpcproxy.InternalResponseHandler;
import tech.pegasys.ethfirewall.jsonrpcproxy.JsonRpcBody;
import tech.pegasys.ethfirewall.jsonrpcproxy.JsonRpcErrorReporter;
import tech.pegasys.ethfirewall.jsonrpcproxy.JsonRpcHttpService;
import tech.pegasys.ethfirewall.jsonrpcproxy.PassThroughHandler;
import tech.pegasys.ethfirewall.jsonrpcproxy.RequestMapper;
Expand All @@ -33,10 +37,13 @@
public class Runner {

private static final Logger LOG = LoggerFactory.getLogger(Runner.class);
private TransactionSigner transactionSigner;
private HttpClientOptions clientOptions;
private HttpServerOptions serverOptions;
private Duration httpRequestTimeout;
private final TransactionSigner transactionSigner;
private final HttpClientOptions clientOptions;
private final HttpServerOptions serverOptions;
private final Duration httpRequestTimeout;
private final HttpResponseFactory responseFactory = new HttpResponseFactory();
private final JsonRpcErrorReporter errorReporter = new JsonRpcErrorReporter(responseFactory);

private Vertx vertx;
private String deploymentId;

Expand All @@ -56,7 +63,7 @@ public void start() {
vertx = Vertx.vertx();
final RequestMapper requestMapper = createRequestMapper(vertx, transactionSigner);
final JsonRpcHttpService httpService =
new JsonRpcHttpService(serverOptions, httpRequestTimeout, requestMapper);
new JsonRpcHttpService(responseFactory, serverOptions, httpRequestTimeout, requestMapper);
vertx.deployVerticle(httpService, this::handleDeployResult);
}

Expand All @@ -71,17 +78,25 @@ private RequestMapper createRequestMapper(

final PassThroughHandler passThroughHandler =
new PassThroughHandler(
errorReporter,
downStreamConnection,
(jsonRpcRequest) -> new JsonRpcBody(Json.encodeToBuffer(jsonRpcRequest)));

final RequestMapper requestMapper = new RequestMapper(passThroughHandler);

final SendTransactionBodyProvider sendTransactionHandler =
new SendTransactionBodyProvider(transactionSigner);

requestMapper.addHandler(
"eth_sendTransaction",
new PassThroughHandler(downStreamConnection, sendTransactionHandler));
new PassThroughHandler(
errorReporter,
downStreamConnection,
new SendTransactionBodyProvider(transactionSigner)));

requestMapper.addHandler(
"eth_accounts",
new InternalResponseHandler(
responseFactory,
new EthAccountsBodyProvider(transactionSigner.getAddress()),
errorReporter));

return requestMapper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
package tech.pegasys.ethfirewall.jsonrpc.response;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
public interface JsonRpcResponse {

@JsonGetter("jsonrpc")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.google.common.base.Objects;

Expand All @@ -23,7 +24,8 @@ public class JsonRpcSuccessResponse implements JsonRpcResponse {
private final Object id;
private final Object result;

public JsonRpcSuccessResponse(final Object id, final Object result) {
public JsonRpcSuccessResponse(
@JsonProperty("id") final Object id, @JsonProperty("result") final Object result) {
this.id = id;
this.result = result;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2019 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.ethfirewall.jsonrpcproxy;

import static java.util.Collections.singletonList;

import tech.pegasys.ethfirewall.jsonrpc.JsonRpcRequest;
import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcError;
import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcErrorResponse;
import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcSuccessResponse;

import java.util.Collection;

import io.vertx.core.json.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EthAccountsBodyProvider implements BodyProvider {

private static final Logger LOG = LoggerFactory.getLogger(EthAccountsBodyProvider.class);

private final String address;

public EthAccountsBodyProvider(final String address) {
this.address = address;
}

@Override
public JsonRpcBody getBody(final JsonRpcRequest request) {
final Object params = request.getParams();

if (isPopulated(params) && isNotEmptyArray(params)) {
LOG.info("eth_accounts should have no parameters, but has {}", request.getParams());
return new JsonRpcBody(
new JsonRpcErrorResponse(request.getId(), JsonRpcError.INVALID_PARAMS));
}

final JsonRpcSuccessResponse response =
new JsonRpcSuccessResponse(request.getId(), singletonList(address));
return new JsonRpcBody(Json.encodeToBuffer(response));
}

private boolean isPopulated(final Object params) {
return params != null;
}

private boolean isNotEmptyArray(final Object params) {
boolean arrayIsEmpty = false;
boolean paramsIsArray = (params instanceof Collection);
if (paramsIsArray) {
Collection<?> collection = (Collection<?>) params;
arrayIsEmpty = collection.isEmpty();
}

return !(paramsIsArray && arrayIsEmpty);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2019 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.ethfirewall.jsonrpcproxy;

import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcResponse;

import io.netty.handler.codec.http.HttpHeaderValues;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.Json;

public class HttpResponseFactory {

private static final String JSON = HttpHeaderValues.APPLICATION_JSON.toString();

public void create(
final HttpServerRequest httpRequest, final int statusCode, final JsonRpcResponse body) {
final HttpServerResponse response = httpRequest.response();

response.putHeader("Content", JSON);
response.setStatusCode(statusCode);
response.setChunked(false);
response.end(Json.encodeToBuffer(body));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2019 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.ethfirewall.jsonrpcproxy;

import tech.pegasys.ethfirewall.jsonrpc.JsonRpcRequest;
import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcSuccessResponse;

import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class InternalResponseHandler implements JsonRpcRequestHandler {

private static final Logger LOG = LoggerFactory.getLogger(InternalResponseHandler.class);

private final HttpResponseFactory responder;
private final BodyProvider responseBodyProvider;
private final JsonRpcErrorReporter errorReporter;

public InternalResponseHandler(
final HttpResponseFactory responder,
final BodyProvider responseBodyProvider,
final JsonRpcErrorReporter errorReporter) {
this.responder = responder;
this.responseBodyProvider = responseBodyProvider;
this.errorReporter = errorReporter;
}

@Override
public void handle(final HttpServerRequest httpServerRequest, final JsonRpcRequest rpcRequest) {
LOG.debug("Internally responding to {}, id={}", rpcRequest.getMethod(), rpcRequest.getId());
final JsonRpcBody providedBody = responseBodyProvider.getBody(rpcRequest);

if (providedBody.hasError()) {
errorReporter.send(rpcRequest, httpServerRequest, providedBody.error());
} else {
final JsonRpcSuccessResponse result =
Json.decodeValue(providedBody.body(), JsonRpcSuccessResponse.class);
responder.create(httpServerRequest, HttpResponseStatus.OK.code(), result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2019 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.ethfirewall.jsonrpcproxy;

import tech.pegasys.ethfirewall.jsonrpc.JsonRpcRequest;
import tech.pegasys.ethfirewall.jsonrpc.response.JsonRpcErrorResponse;

import io.netty.handler.codec.http.HttpResponseStatus;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.json.Json;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JsonRpcErrorReporter {

private static final Logger LOG = LoggerFactory.getLogger(JsonRpcErrorReporter.class);

private final HttpResponseFactory responder;

public JsonRpcErrorReporter(final HttpResponseFactory responder) {
this.responder = responder;
}

void send(
final JsonRpcRequest jsonRequest,
final HttpServerRequest httpRequest,
final JsonRpcErrorResponse error) {
LOG.info("Dropping request from {}", httpRequest.remoteAddress());
LOG.debug(
"Dropping request method: {}, uri: {}, body: {}, Error body: {}",
httpRequest.method(),
httpRequest.absoluteURI(),
Json.encodePrettily(jsonRequest),
Json.encode(error));

responder.create(httpRequest, HttpResponseStatus.BAD_REQUEST.code(), error);
}
}
Loading

0 comments on commit e83a81c

Please sign in to comment.