Skip to content

Commit

Permalink
refactor(rpc): clean-up error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
jurajpiar committed Apr 10, 2024
1 parent 5f582b7 commit 6581123
Show file tree
Hide file tree
Showing 11 changed files with 401 additions and 59 deletions.
Original file line number Diff line number Diff line change
@@ -1,87 +1,74 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception;

import co.rsk.core.exception.InvalidRskAddressException;
import co.rsk.jsonrpc.JsonRpcError;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.googlecode.jsonrpc4j.ErrorResolver;
import org.ethereum.rpc.exception.handlers.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.HashMap;
import java.util.List;

import static javax.xml.bind.DatatypeConverter.printHexBinary;
import java.util.Map;

/**
* Created by mario on 17/10/2016.
*/
public class RskErrorResolver implements ErrorResolver {
private final Map<Class<? extends Throwable>, Class<? extends RskErrorHandler>> errorHandlers;

private static final Logger logger = LoggerFactory.getLogger("web3");
public RskErrorResolver() {
errorHandlers = new HashMap<>();
errorHandlers.put(InvalidRskAddressException.class, InvalidRskAddressExceptionHandler.class);
errorHandlers.put(RskJsonRpcRequestException.class, RskJsonRpcRequestExceptionHandler.class);
errorHandlers.put(InvalidFormatException.class, InvalidFormatExceptionHandler.class);
errorHandlers.put(UnrecognizedPropertyException.class, UnrecognizedPropertyExceptionHandler.class);
errorHandlers.put(UnsupportedOperationException.class, UnsupportedOperationExceptionHandler.class);
}

@Override
public JsonError resolveError(Throwable t, Method method, List<JsonNode> arguments) {
JsonError error;

if (t instanceof InvalidRskAddressException) {
error = new JsonError(
JsonRpcError.INVALID_PARAMS,
"invalid argument 0: hex string has length " + arguments.get(0).asText().replace("0x", "").length() + ", want 40 for RSK address",
null);
} else if (t instanceof RskJsonRpcRequestException) {
RskJsonRpcRequestException rskJsonRpcRequestException = (RskJsonRpcRequestException) t;
byte[] revertData = rskJsonRpcRequestException.getRevertData();
String errorDataHexString = "0x" + printHexBinary(revertData == null ? new byte[]{} : revertData).toLowerCase();
error = new JsonError(rskJsonRpcRequestException.getCode(), t.getMessage(), errorDataHexString);
} else if (t instanceof InvalidFormatException) {
error = new JsonError(JsonRpcError.INTERNAL_ERROR, "Internal server error, probably due to invalid parameter type", null);
} else if (t instanceof UnrecognizedPropertyException) {
error = new JsonError(
JsonRpcError.INVALID_PARAMS,
getExceptionMessage((UnrecognizedPropertyException) t),
null);
} else if (t instanceof JsonMappingException && t.getMessage().contains("Can not construct instance")) {
error = new JsonError(
JsonRpcError.INVALID_PARAMS,
"invalid argument 0: json: cannot unmarshal string into value of input",
null);
} else if (t instanceof UnsupportedOperationException || (t.getMessage() != null && t.getMessage().toLowerCase().contains("method not supported"))) {
error = new JsonError(
JsonRpcError.METHOD_NOT_FOUND,
"the method " + method.getName() + " does not exist/is not available",
null);
} else {
logger.error("JsonRPC error when for method {} with arguments {}", method, arguments, t);
error = new JsonError(JsonRpcError.INTERNAL_ERROR, "Internal server error", null);
public JsonError resolveError(Throwable throwable, Method method, List<JsonNode> arguments) {
if (throwable instanceof JsonMappingException && throwable.getMessage() != null && throwable.getMessage().contains("Can not construct instance")) {
return new JsonMappingExceptionHandler().apply(throwable, method, arguments);
}
return error;
}

private static String getExceptionMessage(UnrecognizedPropertyException ex) {
if (ex.getPropertyName() == null || ex.getKnownPropertyIds() == null) return "Invalid parameters";

StringBuilder stringBuilder = new StringBuilder("Unrecognized field \"");
stringBuilder.append(ex.getPropertyName());
stringBuilder.append("\" (");
stringBuilder.append(ex.getKnownPropertyIds().size());
stringBuilder.append(" known properties: [");
if (throwable.getMessage() != null && throwable.getMessage().toLowerCase().contains("method not supported")) {
return new UnsupportedOperationExceptionHandler().apply(throwable, method, arguments);
}

Iterator<Object> iterator = ex.getKnownPropertyIds().iterator();
while (iterator.hasNext()) {
stringBuilder.append("\"");
stringBuilder.append(iterator.next());
stringBuilder.append("\"");
if (iterator.hasNext()) {
stringBuilder.append(", ");
}
Class<? extends RskErrorHandler> handlerClass = errorHandlers.get(throwable.getClass());
if (handlerClass == null) {
return (new DefaultRskErrorHandler()).apply(throwable, method, arguments);
}

stringBuilder.append("])");
RskErrorHandler handler;
try {
handler = handlerClass.getDeclaredConstructor().newInstance();
} catch (Exception exception) {
return (new DefaultRskErrorHandler()).apply(throwable, method, arguments);
}

return stringBuilder.toString();
return handler.apply(throwable, method, arguments);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception.handlers;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;

public abstract class BaseRskErrorHandler implements RskErrorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger("web3");

protected void logJsonRPCError(Throwable throwable, Method method, List<JsonNode> arguments) {
LOGGER.error("JsonRPC error for method {} with arguments {}", method, arguments, throwable);
}

protected static String getExceptionMessage(UnrecognizedPropertyException ex) {
if (ex.getPropertyName() == null || ex.getKnownPropertyIds() == null) return "Invalid parameters";

StringBuilder stringBuilder = new StringBuilder("Unrecognized field \"");
stringBuilder.append(ex.getPropertyName());
stringBuilder.append("\" (");
stringBuilder.append(ex.getKnownPropertyIds().size());
stringBuilder.append(" known properties: [");

Iterator<Object> iterator = ex.getKnownPropertyIds().iterator();
while (iterator.hasNext()) {
stringBuilder.append("\"");
stringBuilder.append(iterator.next());
stringBuilder.append("\"");
if (iterator.hasNext()) {
stringBuilder.append(", ");
}
}
stringBuilder.append("])");

return stringBuilder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception.handlers;

import co.rsk.jsonrpc.JsonRpcError;
import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.ErrorResolver;
import org.slf4j.Logger;

import java.lang.reflect.Method;
import java.util.List;

public class DefaultRskErrorHandler extends BaseRskErrorHandler {

@Override
public ErrorResolver.JsonError apply(Throwable throwable, Method method, List<JsonNode> arguments) {
logJsonRPCError(throwable, method, arguments);
return new ErrorResolver.JsonError(JsonRpcError.INTERNAL_ERROR, "Internal server error", null);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception.handlers;

import co.rsk.jsonrpc.JsonRpcError;
import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.ErrorResolver;
import org.slf4j.Logger;

import java.lang.reflect.Method;
import java.util.List;

public class InvalidFormatExceptionHandler extends BaseRskErrorHandler {
@Override
public ErrorResolver.JsonError apply(Throwable throwable, Method method, List<JsonNode> arguments) {
logJsonRPCError(throwable, method, arguments);

return new ErrorResolver.JsonError(JsonRpcError.INTERNAL_ERROR, "Internal server error, probably due to invalid parameter type", null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception.handlers;

import co.rsk.jsonrpc.JsonRpcError;
import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.ErrorResolver.JsonError;
import org.slf4j.Logger;

import java.lang.reflect.Method;
import java.util.List;

public class InvalidRskAddressExceptionHandler extends BaseRskErrorHandler {
@Override
public JsonError apply(Throwable throwable, Method method, List<JsonNode> arguments) {
logJsonRPCError(throwable, method, arguments);

return new JsonError(
JsonRpcError.INVALID_PARAMS,
"invalid argument 0: hex string has length " + arguments.get(0).asText().replace("0x", "").length() + ", want 40 for RSK address",
null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception.handlers;

import co.rsk.jsonrpc.JsonRpcError;
import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.ErrorResolver;
import org.slf4j.Logger;

import java.lang.reflect.Method;
import java.util.List;

public class JsonMappingExceptionHandler extends BaseRskErrorHandler {
@Override
public ErrorResolver.JsonError apply(Throwable throwable, Method method, List<JsonNode> arguments) {
logJsonRPCError(throwable, method, arguments);

return new ErrorResolver.JsonError(
JsonRpcError.INVALID_PARAMS,
"invalid argument 0: json: cannot unmarshal string into value of input",
null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* This file is part of RskJ
* Copyright (C) 2018 RSK Labs Ltd.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.rpc.exception.handlers;

import com.fasterxml.jackson.databind.JsonNode;
import com.googlecode.jsonrpc4j.ErrorResolver.JsonError;

import java.lang.reflect.Method;
import java.util.List;

public interface RskErrorHandler {
JsonError apply(Throwable throwable, Method method, List<JsonNode> arguments);
}
Loading

0 comments on commit 6581123

Please sign in to comment.