From d9ff61a00c9e98c079a8d7234c32f1ebf4ccf704 Mon Sep 17 00:00:00 2001 From: Juraj Piar Date: Thu, 4 Apr 2024 21:24:38 +0100 Subject: [PATCH 1/5] feat(rpc): adds revert data to eth_call --- .../co/rsk/rpc/modules/eth/EthModule.java | 37 +++++++------- .../eth/EthModuleTransactionInstant.java | 30 ++++-------- .../co/rsk/rpc/modules/eth/ProgramRevert.java | 47 ++++++++++++++++++ .../rpc/exception/RskErrorResolver.java | 7 ++- .../exception/RskJsonRpcRequestException.java | 44 +++++++++++++---- .../co/rsk/rpc/modules/eth/EthModuleTest.java | 13 +++-- .../rpc/netty/JsonRpcCustomServerTest.java | 40 ++++++++++++++-- .../rpc/exception/RskErrorResolverTest.java | 48 ++++++++++++++++--- 8 files changed, 201 insertions(+), 65 deletions(-) create mode 100644 rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java index 3602f3fa400..2ca79605ef6 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java @@ -137,16 +137,10 @@ public String call(CallArgumentsParam argsParam, BlockIdentifierParam bnOrId) { } else { res = callConstant(args, block); } - if (res.isRevert()) { - Optional revertReason = decodeRevertReason(res); - if (revertReason.isPresent()) { - throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertReason.get()); - } else { - throw RskJsonRpcRequestException.transactionRevertedExecutionError(); - } - } + throw RskJsonRpcRequestException.transactionRevertedExecutionError(decodeProgramRevert(res)); + } hReturn = HexUtils.toUnformattedJsonHex(res.getHReturn()); return hReturn; @@ -299,22 +293,29 @@ public ProgramResult callConstant(CallArguments args, Block executionBlock) { * Look for { Error("msg") } function, if it matches decode the "msg" param. * The 4 first bytes are the function signature. * - * @param res - * @return revert reason, empty if didnt match. + * @param programResult contains the result of execution of a smart contract + * @return revert reason and revert data. reason may be nullable or empty */ - public static Optional decodeRevertReason(ProgramResult res) { - byte[] bytes = res.getHReturn(); - if (bytes == null || bytes.length < 4) { - return Optional.empty(); + public static ProgramRevert decodeProgramRevert(ProgramResult programResult) { + byte[] bytes = programResult.getHReturn(); + if (bytes.length < 4) { + + return new ProgramRevert(bytes); } - final byte[] signature = copyOfRange(res.getHReturn(), 0, 4); + final byte[] signature = copyOfRange(bytes, 0, 4); if (!Arrays.equals(signature, ERROR_ABI_FUNCTION_SIGNATURE)) { - return Optional.empty(); + + return new ProgramRevert(bytes); + } + + final Object[] decode = ERROR_ABI_FUNCTION.decode(bytes); + if (decode == null || decode.length == 0) { + + return new ProgramRevert(bytes); } - final Object[] decode = ERROR_ABI_FUNCTION.decode(res.getHReturn()); - return decode != null && decode.length > 0 ? Optional.of((String) decode[0]) : Optional.empty(); + return new ProgramRevert((String) decode[0], bytes); } private ProgramResult callConstantWithState(CallArguments args, Block executionBlock, Trie state) { diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java index f5b08ae5dfa..3948cdad2e0 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java @@ -18,27 +18,23 @@ package co.rsk.rpc.modules.eth; -import static org.ethereum.rpc.exception.RskJsonRpcRequestException.transactionRevertedExecutionError; -import static org.ethereum.rpc.exception.RskJsonRpcRequestException.unknownError; - -import java.util.Optional; - +import co.rsk.core.Wallet; +import co.rsk.core.bc.BlockExecutor; +import co.rsk.crypto.Keccak256; +import co.rsk.mine.MinerClient; +import co.rsk.mine.MinerServer; +import co.rsk.net.TransactionGateway; +import co.rsk.util.HexUtils; import org.ethereum.config.Constants; import org.ethereum.core.Blockchain; import org.ethereum.core.TransactionPool; import org.ethereum.db.TransactionInfo; -import org.ethereum.rpc.exception.RskJsonRpcRequestException; import org.ethereum.rpc.parameters.CallArgumentsParam; import org.ethereum.rpc.parameters.HexDataParam; import org.ethereum.vm.program.ProgramResult; -import co.rsk.core.Wallet; -import co.rsk.core.bc.BlockExecutor; -import co.rsk.crypto.Keccak256; -import co.rsk.mine.MinerClient; -import co.rsk.mine.MinerServer; -import co.rsk.net.TransactionGateway; -import co.rsk.util.HexUtils; +import static org.ethereum.rpc.exception.RskJsonRpcRequestException.transactionRevertedExecutionError; +import static org.ethereum.rpc.exception.RskJsonRpcRequestException.unknownError; public class EthModuleTransactionInstant extends EthModuleTransactionBase { @@ -115,13 +111,7 @@ private String getReturnMessage(String txHash) { ProgramResult programResult = this.blockExecutor.getProgramResult(hash); if (programResult != null && programResult.isRevert()) { - Optional revertReason = EthModule.decodeRevertReason(programResult); - - if (revertReason.isPresent()) { - throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertReason.get()); - } else { - throw RskJsonRpcRequestException.transactionRevertedExecutionError(); - } + throw transactionRevertedExecutionError(EthModule.decodeProgramRevert(programResult)); } if (!transactionInfo.getReceipt().isSuccessful()) { diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java new file mode 100644 index 00000000000..45fb9679802 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java @@ -0,0 +1,47 @@ +/* + * 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 . + */ +package co.rsk.rpc.modules.eth; + +import javax.annotation.Nonnull; + +public class ProgramRevert { + @Nonnull + private final String reason; + + @Nonnull + private final byte[] data; + + public ProgramRevert(@Nonnull byte[] data) { + this("", data); + } + + public ProgramRevert(@Nonnull String reason, @Nonnull byte[] data) { + this.reason = reason; + this.data = data; + } + + @Nonnull + public String getReason() { + return reason; + } + + @Nonnull + public byte[] getData() { + return data; + } +} \ No newline at end of file diff --git a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java index 7736ceaf598..b74bc17cb56 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java @@ -14,6 +14,8 @@ import java.util.Iterator; import java.util.List; +import static javax.xml.bind.DatatypeConverter.printHexBinary; + /** * Created by mario on 17/10/2016. */ @@ -31,7 +33,10 @@ public JsonError resolveError(Throwable t, Method method, List argumen "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) { - error = new JsonError(((RskJsonRpcRequestException) t).getCode(), t.getMessage(), null); + 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) { diff --git a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java index 0a4cc78e30f..d941202ef2c 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java @@ -1,37 +1,65 @@ package org.ethereum.rpc.exception; +import co.rsk.rpc.modules.eth.ProgramRevert; + +import javax.annotation.Nullable; + public class RskJsonRpcRequestException extends RuntimeException { private final Integer code; - protected RskJsonRpcRequestException(Integer code, String message, Exception e) { + private final byte[] revertData; + + protected RskJsonRpcRequestException(Integer code, byte[] revertData, String message, Exception e) { super(message, e); this.code = code; + this.revertData = revertData; } - public RskJsonRpcRequestException(Integer code, String message) { + protected RskJsonRpcRequestException(Integer code, String message, Exception e) { + this(code, new byte[]{}, message, e); + } + + public RskJsonRpcRequestException(Integer code, byte[] revertData, String message) { super(message); this.code = code; + this.revertData = revertData; + } + + public RskJsonRpcRequestException(Integer code, String message) { + this(code, new byte[]{}, message); } public Integer getCode() { return code; } - public static RskJsonRpcRequestException transactionRevertedExecutionError() { - return executionError("transaction reverted"); + @Nullable + public byte[] getRevertData() { + return revertData; + } + + public static RskJsonRpcRequestException transactionRevertedExecutionError(ProgramRevert programRevert) { + byte[] revertData = programRevert.getData(); + String revertReason = programRevert.getReason(); + if (revertReason.isEmpty()) { + + return executionError("transaction reverted, no reason specified", revertData); + } + + return executionError("revert " + revertReason, revertData); } - public static RskJsonRpcRequestException transactionRevertedExecutionError(String revertReason) { - return executionError("revert " + revertReason); + public static RskJsonRpcRequestException transactionRevertedExecutionError() { + return executionError("transaction reverted", null); } public static RskJsonRpcRequestException unknownError(String message) { return new RskJsonRpcRequestException(-32009, message); } - private static RskJsonRpcRequestException executionError(String message) { - return new RskJsonRpcRequestException(-32015, String.format("VM Exception while processing transaction: %s", message)); + private static RskJsonRpcRequestException executionError(String message, byte[] revertData) { + return new RskJsonRpcRequestException(-32015, revertData, String.format("VM Exception while processing transaction: %s", message)); } public static RskJsonRpcRequestException transactionError(String message) { diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java index b8b740adec1..144a95d214d 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java @@ -160,7 +160,7 @@ void test_revertedTransaction() { .thenReturn(blockResult); when(blockResult.getBlock()).thenReturn(block); - byte[] hreturn = Hex.decode( + byte[] hReturn = Hex.decode( "08c379a000000000000000000000000000000000000000000000000000000000" + "0000002000000000000000000000000000000000000000000000000000000000" + "0000000f6465706f73697420746f6f2062696700000000000000000000000000" + @@ -168,7 +168,7 @@ void test_revertedTransaction() { ProgramResult executorResult = mock(ProgramResult.class); when(executorResult.isRevert()).thenReturn(true); when(executorResult.getHReturn()) - .thenReturn(hreturn); + .thenReturn(hReturn); ReversibleTransactionExecutor executor = mock(ReversibleTransactionExecutor.class); when(executor.executeTransaction(eq(blockResult.getBlock()), any(), any(), any(), any(), any(), any(), any())) @@ -189,11 +189,10 @@ void test_revertedTransaction() { config.getGasEstimationCap(), config.getCallGasCap()); - try { - eth.call(TransactionFactoryHelper.toCallArgumentsParam(args), new BlockIdentifierParam("latest")); - } catch (RskJsonRpcRequestException e) { - assertThat(e.getMessage(), Matchers.containsString("deposit too big")); - } + RskJsonRpcRequestException exception = assertThrows(RskJsonRpcRequestException.class, () -> eth.call(TransactionFactoryHelper.toCallArgumentsParam(args), new BlockIdentifierParam("latest"))); + assertThat(exception.getMessage(), Matchers.containsString("deposit too big")); + assertNotNull(exception.getRevertData()); + assertEquals(hReturn, exception.getRevertData()); } @Test diff --git a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java index 648a1024c37..f756b937be5 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java @@ -22,11 +22,19 @@ import co.rsk.rpc.ModuleDescription; import co.rsk.rpc.exception.JsonRpcResponseLimitError; import co.rsk.rpc.exception.JsonRpcTimeoutError; +import co.rsk.rpc.modules.eth.ProgramRevert; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.googlecode.jsonrpc4j.JsonResponse; +import org.bouncycastle.util.encoders.Hex; +import org.ethereum.rpc.exception.RskErrorResolver; +import org.ethereum.rpc.exception.RskJsonRpcRequestException; +import org.ethereum.rpc.parameters.BlockIdentifierParam; +import org.ethereum.rpc.parameters.CallArgumentsParam; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; @@ -38,10 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class JsonRpcCustomServerTest { @@ -228,6 +233,33 @@ void sendingRequestWithNonDeclaredMethodShouldFail() throws Exception { assertEquals(expectedResponse, actualResponse.getResponse().toString()); } + @Test + void failedEthCall_givenRskErrorResolver_shouldThrowWithData() throws JsonProcessingException { + String jsonRequest = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_call\",\"params\":[{\"to\": \"0x77045E71a7A2c50903d88e564cD72fab11e82051\", \"data\": \"0x00004\"}, \"latest\"],\"id\":1}"; + byte[] revertBytes = Hex.decode( + "08c379a000000000000000000000000000000000000000000000000000000000" + + "0000002000000000000000000000000000000000000000000000000000000000" + + "0000000f6465706f73697420746f6f2062696700000000000000000000000000" + + "00000000"); + String expectedData = "0x" + Hex.toHexString(revertBytes); + JsonNode request = objectMapper.readTree(jsonRequest); + + FakeWeb3ForEthCall web3ForEthCall = mock(FakeWeb3ForEthCall.class); + ProgramRevert fakeProgramRevert = new ProgramRevert("deposit too big", revertBytes); + RskJsonRpcRequestException exception = RskJsonRpcRequestException.transactionRevertedExecutionError(fakeProgramRevert); + when(web3ForEthCall.eth_call(any(), any())).thenThrow(exception); + jsonRpcCustomServer = new JsonRpcCustomServer(web3ForEthCall, FakeWeb3ForEthCall.class, modules, objectMapper); + jsonRpcCustomServer.setErrorResolver(new RskErrorResolver()); + + JsonResponse actualResponse = jsonRpcCustomServer.handleJsonNodeRequest(request); + String actualData = actualResponse.getResponse().get("error").get("data").asText(); + + assertEquals(expectedData, actualData); + } + + public interface FakeWeb3ForEthCall { + String eth_call(CallArgumentsParam args, BlockIdentifierParam bnOrId); + } public interface Web3Test { String test_first(String param1); diff --git a/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java b/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java index 9446261d093..7a76a2cf54c 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java @@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; +import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -33,12 +34,26 @@ void setup() { } @Test - void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorAsExpected() throws NoSuchMethodException { + void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorWithErrorCode() throws NoSuchMethodException { // Given - Integer code = 1; - String message = "message"; - RskJsonRpcRequestException exception = new RskJsonRpcRequestException(code, message); + Integer expectedErrorCode = 1; + RskJsonRpcRequestException exception = new RskJsonRpcRequestException(expectedErrorCode, "message"); + Method methodMock = this.getClass().getMethod("mockMethod"); + List jsonNodeListMock = new ArrayList<>(); + // When + JsonError result = rskErrorResolver.resolveError(exception, methodMock, jsonNodeListMock); + + // Then + Assertions.assertNotNull(result); + Assertions.assertEquals(expectedErrorCode, (Integer) result.code); + } + + @Test + void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorWithErrorMessage() throws NoSuchMethodException { + // Given + String expectedErrorMessage = "message"; + RskJsonRpcRequestException exception = new RskJsonRpcRequestException(1, expectedErrorMessage); Method methodMock = this.getClass().getMethod("mockMethod"); List jsonNodeListMock = new ArrayList<>(); @@ -47,9 +62,28 @@ void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorAsExpecte // Then Assertions.assertNotNull(result); - Assertions.assertEquals(code, (Integer) result.code); - Assertions.assertEquals(message, result.message); - Assertions.assertNull(result.data); + Assertions.assertEquals(expectedErrorMessage, result.message); + } + + @Test + void test_resolveError_givenRskJsonRpcRequestExceptionWithData_returnsJsonErrorWithDataAsHexString() throws NoSuchMethodException { + // Given + String dataString = "08c379a000000000000000000000000000000000000000000000000000000000" + + "0000002000000000000000000000000000000000000000000000000000000000" + + "0000000f6465706f73697420746f6f2062696700000000000000000000000000" + + "00000000"; + byte[] data = Hex.decode(dataString); + RskJsonRpcRequestException exception = new RskJsonRpcRequestException(1, data, "message"); + Method methodMock = this.getClass().getMethod("mockMethod"); + List jsonNodeListMock = new ArrayList<>(); + + // When + JsonError result = rskErrorResolver.resolveError(exception, methodMock, jsonNodeListMock); + + // Then + String expectedData = "0x" + dataString; + Assertions.assertNotNull(result); + Assertions.assertEquals(expectedData, result.data); } @Test From f981ed41dc7bb1f03f9ff9353e0af2a7316287bc Mon Sep 17 00:00:00 2001 From: Juraj Piar Date: Wed, 10 Apr 2024 10:59:24 +0100 Subject: [PATCH 2/5] fix(test): releases response size limit ctx after test --- .../java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java index f756b937be5..8d4d20c9655 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java @@ -76,12 +76,13 @@ void testHandleJsonNodeRequest_WithResponseLimit() throws Exception { JsonNode request = objectMapper.readTree(FIRST_METHOD_REQUEST); Web3Test handler = mock(Web3Test.class); //expected response would be {"jsonrpc":"2.0","id":1,"result":"test_method_response"} with 56 bytes - ResponseSizeLimitContext.createResponseSizeContext(55); + ResponseSizeLimitContext limitContext = ResponseSizeLimitContext.createResponseSizeContext(55); jsonRpcCustomServer = new JsonRpcCustomServer(handler, Web3Test.class, modules, objectMapper); when(handler.test_first(anyString())).thenReturn(response); assertThrows(JsonRpcResponseLimitError.class, () -> jsonRpcCustomServer.handleJsonNodeRequest(request)); + limitContext.close(); } @Test @@ -247,7 +248,9 @@ void failedEthCall_givenRskErrorResolver_shouldThrowWithData() throws JsonProces FakeWeb3ForEthCall web3ForEthCall = mock(FakeWeb3ForEthCall.class); ProgramRevert fakeProgramRevert = new ProgramRevert("deposit too big", revertBytes); RskJsonRpcRequestException exception = RskJsonRpcRequestException.transactionRevertedExecutionError(fakeProgramRevert); - when(web3ForEthCall.eth_call(any(), any())).thenThrow(exception); + when(web3ForEthCall.eth_call(any(), any())).then(invocation -> { + throw exception; + }); jsonRpcCustomServer = new JsonRpcCustomServer(web3ForEthCall, FakeWeb3ForEthCall.class, modules, objectMapper); jsonRpcCustomServer.setErrorResolver(new RskErrorResolver()); From f38eefd1e112a2d86b74af878e853c1ab2f597e1 Mon Sep 17 00:00:00 2001 From: Juraj Piar Date: Wed, 10 Apr 2024 12:54:09 +0100 Subject: [PATCH 3/5] refactor(rpc): clean-up error handling --- .../co/rsk/rpc/modules/eth/EthModule.java | 40 ++++++++++++---- .../eth/EthModuleTransactionInstant.java | 15 +++++- .../co/rsk/rpc/modules/eth/ProgramRevert.java | 47 ------------------- .../rpc/exception/RskErrorResolver.java | 5 +- .../exception/RskJsonRpcRequestException.java | 35 +++++++------- .../co/rsk/rpc/modules/eth/EthModuleTest.java | 13 ++++- .../rpc/netty/JsonRpcCustomServerTest.java | 17 ++++--- .../rpc/exception/RskErrorResolverTest.java | 40 +++++++--------- 8 files changed, 101 insertions(+), 111 deletions(-) delete mode 100644 rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java index 2ca79605ef6..acb5e1d2eb9 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java @@ -33,7 +33,14 @@ import co.rsk.trie.TrieStoreImpl; import co.rsk.util.HexUtils; import com.google.common.annotations.VisibleForTesting; -import org.ethereum.core.*; +import org.apache.commons.lang3.tuple.Pair; +import org.ethereum.core.Block; +import org.ethereum.core.Blockchain; +import org.ethereum.core.CallTransaction; +import org.ethereum.core.Repository; +import org.ethereum.core.Transaction; +import org.ethereum.core.TransactionExecutor; +import org.ethereum.core.TransactionPool; import org.ethereum.datasource.HashMapDB; import org.ethereum.db.MutableRepository; import org.ethereum.rpc.CallArguments; @@ -51,7 +58,9 @@ import javax.annotation.Nonnull; import java.io.IOException; -import java.util.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import static java.util.Arrays.copyOfRange; import static org.ethereum.rpc.exception.RskJsonRpcRequestException.invalidParamError; @@ -138,13 +147,24 @@ public String call(CallArgumentsParam argsParam, BlockIdentifierParam bnOrId) { res = callConstant(args, block); } if (res.isRevert()) { + Pair programRevert = decodeProgramRevert(res); + String revertReason = programRevert.getLeft(); + byte[] revertData = programRevert.getRight(); + if (revertData == null) { + throw RskJsonRpcRequestException.transactionRevertedExecutionError(); + } + + if (revertReason == null) { + throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertData); + } - throw RskJsonRpcRequestException.transactionRevertedExecutionError(decodeProgramRevert(res)); + throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertReason, revertData); } hReturn = HexUtils.toUnformattedJsonHex(res.getHReturn()); return hReturn; - } finally { + } + finally { LOGGER.debug("eth_call(): {}", hReturn); } } @@ -296,26 +316,26 @@ public ProgramResult callConstant(CallArguments args, Block executionBlock) { * @param programResult contains the result of execution of a smart contract * @return revert reason and revert data. reason may be nullable or empty */ - public static ProgramRevert decodeProgramRevert(ProgramResult programResult) { + public static Pair decodeProgramRevert(ProgramResult programResult) { byte[] bytes = programResult.getHReturn(); - if (bytes.length < 4) { + if (bytes == null || bytes.length < 4) { - return new ProgramRevert(bytes); + return Pair.of(null, null); } final byte[] signature = copyOfRange(bytes, 0, 4); if (!Arrays.equals(signature, ERROR_ABI_FUNCTION_SIGNATURE)) { - return new ProgramRevert(bytes); + return Pair.of(null, bytes); } final Object[] decode = ERROR_ABI_FUNCTION.decode(bytes); if (decode == null || decode.length == 0) { - return new ProgramRevert(bytes); + return Pair.of(null, bytes); } - return new ProgramRevert((String) decode[0], bytes); + return Pair.of((String) decode[0], bytes); } private ProgramResult callConstantWithState(CallArguments args, Block executionBlock, Trie state) { diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java index 3948cdad2e0..77d3d903f01 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModuleTransactionInstant.java @@ -25,10 +25,12 @@ import co.rsk.mine.MinerServer; import co.rsk.net.TransactionGateway; import co.rsk.util.HexUtils; +import org.apache.commons.lang3.tuple.Pair; import org.ethereum.config.Constants; import org.ethereum.core.Blockchain; import org.ethereum.core.TransactionPool; import org.ethereum.db.TransactionInfo; +import org.ethereum.rpc.exception.RskJsonRpcRequestException; import org.ethereum.rpc.parameters.CallArgumentsParam; import org.ethereum.rpc.parameters.HexDataParam; import org.ethereum.vm.program.ProgramResult; @@ -111,7 +113,18 @@ private String getReturnMessage(String txHash) { ProgramResult programResult = this.blockExecutor.getProgramResult(hash); if (programResult != null && programResult.isRevert()) { - throw transactionRevertedExecutionError(EthModule.decodeProgramRevert(programResult)); + Pair programRevert = EthModule.decodeProgramRevert(programResult); + String revertReason = programRevert.getLeft(); + byte[] revertData = programRevert.getRight(); + if (revertData == null) { + throw RskJsonRpcRequestException.transactionRevertedExecutionError(); + } + + if (revertReason == null) { + throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertData); + } + + throw RskJsonRpcRequestException.transactionRevertedExecutionError(revertReason, revertData); } if (!transactionInfo.getReceipt().isSuccessful()) { diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java deleted file mode 100644 index 45fb9679802..00000000000 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/ProgramRevert.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 . - */ -package co.rsk.rpc.modules.eth; - -import javax.annotation.Nonnull; - -public class ProgramRevert { - @Nonnull - private final String reason; - - @Nonnull - private final byte[] data; - - public ProgramRevert(@Nonnull byte[] data) { - this("", data); - } - - public ProgramRevert(@Nonnull String reason, @Nonnull byte[] data) { - this.reason = reason; - this.data = data; - } - - @Nonnull - public String getReason() { - return reason; - } - - @Nonnull - public byte[] getData() { - return data; - } -} \ No newline at end of file diff --git a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java index b74bc17cb56..601ca5d4d90 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskErrorResolver.java @@ -2,6 +2,7 @@ import co.rsk.core.exception.InvalidRskAddressException; import co.rsk.jsonrpc.JsonRpcError; +import co.rsk.util.HexUtils; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.exc.InvalidFormatException; @@ -14,8 +15,6 @@ import java.util.Iterator; import java.util.List; -import static javax.xml.bind.DatatypeConverter.printHexBinary; - /** * Created by mario on 17/10/2016. */ @@ -35,7 +34,7 @@ public JsonError resolveError(Throwable t, Method method, List argumen } else if (t instanceof RskJsonRpcRequestException) { RskJsonRpcRequestException rskJsonRpcRequestException = (RskJsonRpcRequestException) t; byte[] revertData = rskJsonRpcRequestException.getRevertData(); - String errorDataHexString = "0x" + printHexBinary(revertData == null ? new byte[]{} : revertData).toLowerCase(); + String errorDataHexString = revertData == null ? null : HexUtils.toUnformattedJsonHex(revertData); 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); diff --git a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java index d941202ef2c..a5a7578f540 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java @@ -1,16 +1,15 @@ package org.ethereum.rpc.exception; -import co.rsk.rpc.modules.eth.ProgramRevert; - +import javax.annotation.Nonnull; import javax.annotation.Nullable; public class RskJsonRpcRequestException extends RuntimeException { - private final Integer code; + @Nullable private final byte[] revertData; - protected RskJsonRpcRequestException(Integer code, byte[] revertData, String message, Exception e) { + protected RskJsonRpcRequestException(Integer code, @Nullable byte[] revertData, String message, Exception e) { super(message, e); this.code = code; this.revertData = revertData; @@ -20,7 +19,7 @@ protected RskJsonRpcRequestException(Integer code, String message, Exception e) this(code, new byte[]{}, message, e); } - public RskJsonRpcRequestException(Integer code, byte[] revertData, String message) { + public RskJsonRpcRequestException(Integer code, @Nullable byte[] revertData, String message) { super(message); this.code = code; this.revertData = revertData; @@ -34,24 +33,28 @@ public Integer getCode() { return code; } - @Nullable public byte[] getRevertData() { return revertData; } - public static RskJsonRpcRequestException transactionRevertedExecutionError(ProgramRevert programRevert) { - byte[] revertData = programRevert.getData(); - String revertReason = programRevert.getReason(); - if (revertReason.isEmpty()) { - - return executionError("transaction reverted, no reason specified", revertData); - } + public static RskJsonRpcRequestException transactionRevertedExecutionError() { + return executionError("transaction reverted", null); + } - return executionError("revert " + revertReason, revertData); + public static RskJsonRpcRequestException transactionRevertedExecutionError(@Nonnull byte[] revertData) { + return executionError("transaction reverted", revertData); } - public static RskJsonRpcRequestException transactionRevertedExecutionError() { - return executionError("transaction reverted", null); + public static RskJsonRpcRequestException transactionRevertedExecutionError( + @Nonnull String revertReason, + @Nonnull byte[] revertData + ) { + return executionError( + revertReason.isEmpty() + ? "transaction reverted, no reason specified" + : "revert " + revertReason, + revertData + ); } public static RskJsonRpcRequestException unknownError(String message) { diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java index 144a95d214d..b9300c268cc 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java @@ -189,10 +189,19 @@ void test_revertedTransaction() { config.getGasEstimationCap(), config.getCallGasCap()); - RskJsonRpcRequestException exception = assertThrows(RskJsonRpcRequestException.class, () -> eth.call(TransactionFactoryHelper.toCallArgumentsParam(args), new BlockIdentifierParam("latest"))); + BlockIdentifierParam blockIdentifierParam = new BlockIdentifierParam("latest"); + + CallArgumentsParam callArgumentsParam = TransactionFactoryHelper.toCallArgumentsParam(args); + RskJsonRpcRequestException exception = assertThrows( + RskJsonRpcRequestException.class, + () -> eth.call( + callArgumentsParam, + blockIdentifierParam + ) + ); assertThat(exception.getMessage(), Matchers.containsString("deposit too big")); assertNotNull(exception.getRevertData()); - assertEquals(hReturn, exception.getRevertData()); + assertArrayEquals(hReturn, exception.getRevertData()); } @Test diff --git a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java index 8d4d20c9655..53584064197 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java @@ -22,7 +22,6 @@ import co.rsk.rpc.ModuleDescription; import co.rsk.rpc.exception.JsonRpcResponseLimitError; import co.rsk.rpc.exception.JsonRpcTimeoutError; -import co.rsk.rpc.modules.eth.ProgramRevert; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -34,7 +33,6 @@ import org.ethereum.rpc.parameters.CallArgumentsParam; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; @@ -46,7 +44,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.*; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class JsonRpcCustomServerTest { @@ -93,7 +95,7 @@ void testHandleJsonNodeRequest_WithMethodModule() throws Exception { String response = "test_method_response"; when(handler.test_second("param", "param2")).thenAnswer(invocation -> { - waitFor(150); + waitFor(150); return response; }); @@ -246,11 +248,8 @@ void failedEthCall_givenRskErrorResolver_shouldThrowWithData() throws JsonProces JsonNode request = objectMapper.readTree(jsonRequest); FakeWeb3ForEthCall web3ForEthCall = mock(FakeWeb3ForEthCall.class); - ProgramRevert fakeProgramRevert = new ProgramRevert("deposit too big", revertBytes); - RskJsonRpcRequestException exception = RskJsonRpcRequestException.transactionRevertedExecutionError(fakeProgramRevert); - when(web3ForEthCall.eth_call(any(), any())).then(invocation -> { - throw exception; - }); + RskJsonRpcRequestException exception = RskJsonRpcRequestException.transactionRevertedExecutionError("deposit too big", revertBytes); + when(web3ForEthCall.eth_call(any(), any())).thenThrow(exception); jsonRpcCustomServer = new JsonRpcCustomServer(web3ForEthCall, FakeWeb3ForEthCall.class, modules, objectMapper); jsonRpcCustomServer.setErrorResolver(new RskErrorResolver()); diff --git a/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java b/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java index 7a76a2cf54c..53ca05d1604 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java @@ -7,10 +7,10 @@ import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.ser.DefaultSerializerProvider; -import org.bouncycastle.util.encoders.Hex; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.spongycastle.util.encoders.Hex; import java.lang.reflect.Method; import java.util.ArrayList; @@ -34,26 +34,13 @@ void setup() { } @Test - void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorWithErrorCode() throws NoSuchMethodException { + void test_resolveError_givenRskJsonRpcRequestExceptionWithoutData_returnsJsonErrorAsExpected() throws NoSuchMethodException { // Given - Integer expectedErrorCode = 1; - RskJsonRpcRequestException exception = new RskJsonRpcRequestException(expectedErrorCode, "message"); - Method methodMock = this.getClass().getMethod("mockMethod"); - List jsonNodeListMock = new ArrayList<>(); - - // When - JsonError result = rskErrorResolver.resolveError(exception, methodMock, jsonNodeListMock); - - // Then - Assertions.assertNotNull(result); - Assertions.assertEquals(expectedErrorCode, (Integer) result.code); - } + Integer code = 1; + String message = "message"; + String expectedData = "0x"; + RskJsonRpcRequestException exception = new RskJsonRpcRequestException(code, message); - @Test - void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorWithErrorMessage() throws NoSuchMethodException { - // Given - String expectedErrorMessage = "message"; - RskJsonRpcRequestException exception = new RskJsonRpcRequestException(1, expectedErrorMessage); Method methodMock = this.getClass().getMethod("mockMethod"); List jsonNodeListMock = new ArrayList<>(); @@ -62,18 +49,24 @@ void test_resolveError_givenRskJsonRpcRequestException_returnsJsonErrorWithError // Then Assertions.assertNotNull(result); - Assertions.assertEquals(expectedErrorMessage, result.message); + Assertions.assertEquals(code, (Integer) result.code); + Assertions.assertEquals(message, result.message); + Assertions.assertEquals(expectedData, result.data); } @Test - void test_resolveError_givenRskJsonRpcRequestExceptionWithData_returnsJsonErrorWithDataAsHexString() throws NoSuchMethodException { + void test_resolveError_givenRskJsonRpcRequestExceptionWithData_returnsJsonErrorAsExpected() throws NoSuchMethodException { // Given + Integer code = 1; + String message = "message"; String dataString = "08c379a000000000000000000000000000000000000000000000000000000000" + "0000002000000000000000000000000000000000000000000000000000000000" + "0000000f6465706f73697420746f6f2062696700000000000000000000000000" + "00000000"; byte[] data = Hex.decode(dataString); - RskJsonRpcRequestException exception = new RskJsonRpcRequestException(1, data, "message"); + String expectedData = "0x" + dataString; + RskJsonRpcRequestException exception = new RskJsonRpcRequestException(code, data, message); + Method methodMock = this.getClass().getMethod("mockMethod"); List jsonNodeListMock = new ArrayList<>(); @@ -81,8 +74,9 @@ void test_resolveError_givenRskJsonRpcRequestExceptionWithData_returnsJsonErrorW JsonError result = rskErrorResolver.resolveError(exception, methodMock, jsonNodeListMock); // Then - String expectedData = "0x" + dataString; Assertions.assertNotNull(result); + Assertions.assertEquals(code, (Integer) result.code); + Assertions.assertEquals(message, result.message); Assertions.assertEquals(expectedData, result.data); } From 53a421d12ce3d6eaf135ab5162acd186dee8fbf0 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 19 Apr 2024 15:11:39 +0300 Subject: [PATCH 4/5] fix: allow data with size lower than 4 bytes --- .../src/main/java/co/rsk/rpc/modules/eth/EthModule.java | 7 ++++++- .../rpc/exception/RskJsonRpcRequestException.java | 8 +++----- .../org/ethereum/rpc/exception/RskErrorResolverTest.java | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java index acb5e1d2eb9..e7e0f978b0b 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/eth/EthModule.java @@ -318,11 +318,16 @@ public ProgramResult callConstant(CallArguments args, Block executionBlock) { */ public static Pair decodeProgramRevert(ProgramResult programResult) { byte[] bytes = programResult.getHReturn(); - if (bytes == null || bytes.length < 4) { + if (bytes == null) { return Pair.of(null, null); } + if (bytes.length < 4) { + + return Pair.of(null, bytes); + } + final byte[] signature = copyOfRange(bytes, 0, 4); if (!Arrays.equals(signature, ERROR_ABI_FUNCTION_SIGNATURE)) { diff --git a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java index a5a7578f540..bab3bceb406 100644 --- a/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java +++ b/rskj-core/src/main/java/org/ethereum/rpc/exception/RskJsonRpcRequestException.java @@ -16,7 +16,7 @@ protected RskJsonRpcRequestException(Integer code, @Nullable byte[] revertData, } protected RskJsonRpcRequestException(Integer code, String message, Exception e) { - this(code, new byte[]{}, message, e); + this(code, null, message, e); } public RskJsonRpcRequestException(Integer code, @Nullable byte[] revertData, String message) { @@ -26,7 +26,7 @@ public RskJsonRpcRequestException(Integer code, @Nullable byte[] revertData, Str } public RskJsonRpcRequestException(Integer code, String message) { - this(code, new byte[]{}, message); + this(code, null, message); } public Integer getCode() { @@ -50,9 +50,7 @@ public static RskJsonRpcRequestException transactionRevertedExecutionError( @Nonnull byte[] revertData ) { return executionError( - revertReason.isEmpty() - ? "transaction reverted, no reason specified" - : "revert " + revertReason, + "revert " + revertReason, revertData ); } diff --git a/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java b/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java index 53ca05d1604..08ebd954a25 100644 --- a/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java +++ b/rskj-core/src/test/java/org/ethereum/rpc/exception/RskErrorResolverTest.java @@ -38,7 +38,7 @@ void test_resolveError_givenRskJsonRpcRequestExceptionWithoutData_returnsJsonErr // Given Integer code = 1; String message = "message"; - String expectedData = "0x"; + String expectedData = null; RskJsonRpcRequestException exception = new RskJsonRpcRequestException(code, message); Method methodMock = this.getClass().getMethod("mockMethod"); From fad6deb290f01a250fa17d2324dba9f3e6aebde7 Mon Sep 17 00:00:00 2001 From: Volodymyr Kravets Date: Fri, 19 Apr 2024 18:09:07 +0300 Subject: [PATCH 5/5] add test to cover short revert data --- .../co/rsk/rpc/modules/eth/EthModuleTest.java | 51 +++++++++++++++++++ .../rpc/netty/JsonRpcCustomServerTest.java | 10 ++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java index b9300c268cc..a4bcbf608cc 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/eth/EthModuleTest.java @@ -204,6 +204,57 @@ void test_revertedTransaction() { assertArrayEquals(hReturn, exception.getRevertData()); } + @Test + void test_revertedTransactionWithNoRevertDataOrSizeLowerThan4() { + CallArguments args = new CallArguments(); + ExecutionBlockRetriever.Result blockResult = mock(ExecutionBlockRetriever.Result.class); + Block block = mock(Block.class); + ExecutionBlockRetriever retriever = mock(ExecutionBlockRetriever.class); + when(retriever.retrieveExecutionBlock("latest")) + .thenReturn(blockResult); + when(blockResult.getBlock()).thenReturn(block); + + ProgramResult executorResult = mock(ProgramResult.class); + when(executorResult.isRevert()).thenReturn(true); + + ReversibleTransactionExecutor executor = mock(ReversibleTransactionExecutor.class); + when(executor.executeTransaction(eq(blockResult.getBlock()), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(executorResult); + + EthModule eth = new EthModule( + null, + (byte) 0, + null, + null, + executor, + retriever, + null, + null, + null, + new BridgeSupportFactory( + null, null, null, signatureCache), + config.getGasEstimationCap(), + config.getCallGasCap()); + + BlockIdentifierParam blockIdentifierParam = new BlockIdentifierParam("latest"); + + CallArgumentsParam callArgumentsParam = TransactionFactoryHelper.toCallArgumentsParam(args); + + List hReturns = Arrays.asList(null, new byte[0], Hex.decode("08"), Hex.decode("08c3"), Hex.decode("08c379")); + for (byte[] hReturn : hReturns) { + when(executorResult.getHReturn()).thenReturn(hReturn); + + RskJsonRpcRequestException exception = assertThrows( + RskJsonRpcRequestException.class, + () -> eth.call( + callArgumentsParam, + blockIdentifierParam + ) + ); + assertArrayEquals(hReturn, exception.getRevertData()); + } + } + @Test void sendTransactionWithGasLimitTest() { diff --git a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java index 53584064197..6f701929270 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/netty/JsonRpcCustomServerTest.java @@ -78,13 +78,13 @@ void testHandleJsonNodeRequest_WithResponseLimit() throws Exception { JsonNode request = objectMapper.readTree(FIRST_METHOD_REQUEST); Web3Test handler = mock(Web3Test.class); //expected response would be {"jsonrpc":"2.0","id":1,"result":"test_method_response"} with 56 bytes - ResponseSizeLimitContext limitContext = ResponseSizeLimitContext.createResponseSizeContext(55); - jsonRpcCustomServer = new JsonRpcCustomServer(handler, Web3Test.class, modules, objectMapper); + try (ResponseSizeLimitContext ignored = ResponseSizeLimitContext.createResponseSizeContext(55)) { + jsonRpcCustomServer = new JsonRpcCustomServer(handler, Web3Test.class, modules, objectMapper); - when(handler.test_first(anyString())).thenReturn(response); + when(handler.test_first(anyString())).thenReturn(response); - assertThrows(JsonRpcResponseLimitError.class, () -> jsonRpcCustomServer.handleJsonNodeRequest(request)); - limitContext.close(); + assertThrows(JsonRpcResponseLimitError.class, () -> jsonRpcCustomServer.handleJsonNodeRequest(request)); + } } @Test