diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DebugModuleImpl.java b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DebugModuleImpl.java index 88bce9f9f6c..c90d0be1ddd 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DebugModuleImpl.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DebugModuleImpl.java @@ -70,7 +70,7 @@ public JsonNode traceTransaction(String transactionHash, Map tra TraceOptions options = new TraceOptions(traceOptions); - if (options.getUnsupportedOptions().size() > 0) { + if (!options.getUnsupportedOptions().isEmpty()) { // TODO: implement the logic that takes into account the remaining trace options. logger.warn( "Received {} unsupported trace options.", @@ -102,7 +102,7 @@ public JsonNode traceBlock(String blockHash, Map traceOptions) { TraceOptions options = new TraceOptions(traceOptions); - if (options.getUnsupportedOptions().size() > 0) { + if (!options.getUnsupportedOptions().isEmpty()) { // TODO: implement the logic that takes into account the remaining trace options. logger.warn( "Received {} unsupported trace options.", diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DisableOption.java b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DisableOption.java new file mode 100644 index 00000000000..6fac1c24916 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/DisableOption.java @@ -0,0 +1,34 @@ +/* + * This file is part of RskJ + * Copyright (C) 2017 RSK Labs Ltd. + * (derived from ethereumJ library, Copyright (c) 2016 ) + * + * 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.debug; + +public enum DisableOption { + DISABLE_MEMORY("disableMemory", "memory"), + DISABLE_STACK("disableStack", "stack"), + DISABLE_STORAGE("disableStorage", "storage"); + + public final String option; + public final String value; + + DisableOption(String option, String value) { + this.option = option; + this.value = value; + } +} diff --git a/rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java index e24b2c5ada6..b5d3fa3cc40 100644 --- a/rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java @@ -20,46 +20,49 @@ package co.rsk.rpc.modules.debug; import java.util.*; +import java.util.stream.Collectors; public class TraceOptions { - private Set disabledFields; - private Set unsupportedOptions; + private final List supportedOptions; + private final Set disabledFields; + private final Set unsupportedOptions; - private TraceOptions() {} + public TraceOptions() { + supportedOptions = Arrays.stream(DisableOption.values()).map(option -> option.option) + .collect(Collectors.toList()); + + this.disabledFields = new HashSet<>(); + this.unsupportedOptions = new HashSet<>(); + } public TraceOptions(Map traceOptions) { - disabledFields = new HashSet<>(); - unsupportedOptions = new HashSet<>(); - if (traceOptions != null) { - if (traceOptions.containsKey("disableStorage")) { - if (Boolean.parseBoolean(traceOptions.get("disableStorage"))) { - disabledFields.add("storage"); - } - traceOptions.remove("disableStorage"); - } - if (traceOptions.containsKey("disableMemory")) { - if (Boolean.parseBoolean(traceOptions.get("disableMemory"))) { - disabledFields.add("memory"); - } - traceOptions.remove("disableMemory"); - } - if (traceOptions.containsKey("disableStack")) { - if (Boolean.parseBoolean(traceOptions.get("disableStack"))) { - disabledFields.add("stack"); - } - traceOptions.remove("disableStack"); + this(); + + if (traceOptions == null || traceOptions.isEmpty()) return; + + // Disabled Fields Parsing + + for (DisableOption disableOption : DisableOption.values()) { + if (Boolean.parseBoolean(traceOptions.get(disableOption.option))) { + this.disabledFields.add(disableOption.value); } - unsupportedOptions = traceOptions.keySet(); } + + // Unsupported Options + + traceOptions.keySet() + .stream() + .filter(key -> supportedOptions.stream().noneMatch(option -> option.equals(key))) + .forEach(unsupportedOptions::add); } public Set getDisabledFields() { - return disabledFields; + return Collections.unmodifiableSet(disabledFields); } public Set getUnsupportedOptions() { - return unsupportedOptions; + return Collections.unmodifiableSet(unsupportedOptions); } } diff --git a/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java b/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java index 4dda00066e0..747b820de90 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java +++ b/rskj-core/src/main/java/org/ethereum/vm/trace/ProgramTraceProcessor.java @@ -35,18 +35,15 @@ */ public class ProgramTraceProcessor { - private static final ObjectMapper OBJECT_MAPPER = makeObjectMapper(); - private final Map traces = new HashMap<>(); - private TraceOptions traceOptions; + private final TraceOptions traceOptions; public ProgramTraceProcessor() { - traceOptions = new TraceOptions(Collections.emptyMap()); + traceOptions = new TraceOptions(); } public ProgramTraceProcessor(TraceOptions options) { - this(); traceOptions = options; } @@ -68,7 +65,7 @@ public JsonNode getProgramTracesAsJsonNode(List txHashes) { filterProvider.addFilter("opFilter", SimpleBeanPropertyFilter.serializeAllExcept(traceOptions.getDisabledFields())); - return OBJECT_MAPPER.setFilterProvider(filterProvider).valueToTree(txTraces); + return makeObjectMapper().setFilterProvider(filterProvider).valueToTree(txTraces); } public JsonNode getProgramTraceAsJsonNode(Keccak256 txHash) { diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/debug/DebugModuleImplTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/debug/DebugModuleImplTest.java index 4b7cd6451a9..417db6f63a1 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/debug/DebugModuleImplTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/debug/DebugModuleImplTest.java @@ -15,6 +15,7 @@ * 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.debug; import co.rsk.net.MessageHandler; @@ -36,8 +37,9 @@ import org.junit.Before; import org.junit.Test; -import java.io.IOException; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import static org.ethereum.rpc.TypeConverter.stringHexToByteArray; @@ -52,7 +54,7 @@ public class DebugModuleImplTest { private DebugModuleImpl debugModule; @Before - public void setup(){ + public void setup() { blockStore = Web3Mocks.getMockBlockStore(); receiptStore = Web3Mocks.getMockReceiptStore(); messageHandler = Web3Mocks.getMockMessageHandler(); @@ -61,7 +63,7 @@ public void setup(){ } @Test - public void debug_wireProtocolQueueSize_basic() throws IOException { + public void debug_wireProtocolQueueSize_basic() { String result = debugModule.wireProtocolQueueSize(); try { TypeConverter.JSonHexToLong(result); @@ -71,7 +73,7 @@ public void debug_wireProtocolQueueSize_basic() throws IOException { } @Test - public void debug_wireProtocolQueueSize_value() throws IOException { + public void debug_wireProtocolQueueSize_value() { when(messageHandler.getMessageQueueSize()).thenReturn(5L); String result = debugModule.wireProtocolQueueSize(); try { @@ -83,7 +85,7 @@ public void debug_wireProtocolQueueSize_value() throws IOException { } @Test - public void debug_traceTransaction_retrieveUnknownTransactionAsNull() throws Exception { + public void debug_traceTransaction_retrieveUnknownTransactionAsNull() { byte[] hash = stringHexToByteArray("0x00"); when(receiptStore.getInMainChain(hash, blockStore)).thenReturn(Optional.empty()); @@ -212,13 +214,16 @@ public void debug_traceTransaction_retrieveSimpleAccountTransferWithTraceOptions Assert.assertEquals(resultWithNoOptions, resultWithEmptyOptions); - JsonNode resultWithNonEmptyOptions = debugModule.traceTransaction(transaction.getHash().toJsonString(), Collections.singletonMap("disableStorage", "true")); + Map traceOptions = new HashMap<>(); + traceOptions.put("disableStorage", "true"); + + JsonNode resultWithNonEmptyOptions = debugModule.traceTransaction(transaction.getHash().toJsonString(), traceOptions); Assert.assertEquals(resultWithNoOptions, resultWithNonEmptyOptions); } @Test - public void debug_traceBlock_retrieveUnknownBlockAsNull() throws Exception { + public void debug_traceBlock_retrieveUnknownBlockAsNull() { byte[] hash = stringHexToByteArray("0x00"); when(blockStore.getBlockByHash(hash)).thenReturn(null); @@ -256,4 +261,58 @@ public void debug_traceBlock_retrieveSimpleContractsCreationTrace() throws Excep Assert.assertTrue(structLogs.size() > 0); }); } + + @Test + public void debug_traceTransaction_retrieveSimpleContractInvocationTrace_traceOptions_disableAllFields_OK() throws Exception { + DslParser parser = DslParser.fromResource("dsl/contracts02.txt"); + ReceiptStore receiptStore = new ReceiptStoreImpl(new HashMapDB()); + World world = new World(receiptStore); + + WorldDslProcessor processor = new WorldDslProcessor(world); + processor.processCommands(parser); + + Transaction transaction = world.getTransactionByName("tx02"); + + DebugModuleImpl debugModule = new DebugModuleImpl(world.getBlockStore(), receiptStore, messageHandler, world.getBlockExecutor()); + + Map traceOptions = new HashMap<>(); + traceOptions.put("disableStack", "true"); + traceOptions.put("disableMemory", "true"); + traceOptions.put("disableStorage", "true"); + + JsonNode witnessResult = debugModule.traceTransaction(transaction.getHash().toJsonString(), null); + JsonNode result = debugModule.traceTransaction(transaction.getHash().toJsonString(), traceOptions); + + // Sanity Check + + Assert.assertNotNull(witnessResult); + Assert.assertTrue(witnessResult.isObject()); + + ObjectNode oWitnessResult = (ObjectNode) witnessResult; + Assert.assertTrue(oWitnessResult.get("error").textValue().isEmpty()); + Assert.assertTrue(oWitnessResult.get("result").textValue().isEmpty()); + JsonNode witnessStructLogs = oWitnessResult.get("structLogs"); + Assert.assertTrue(witnessStructLogs.isArray()); + Assert.assertTrue(witnessStructLogs.size() > 0); + + Assert.assertNotNull(result); + Assert.assertTrue(result.isObject()); + + ObjectNode oResult = (ObjectNode) result; + Assert.assertTrue(oResult.get("error").textValue().isEmpty()); + Assert.assertTrue(oResult.get("result").textValue().isEmpty()); + JsonNode structLogs = oResult.get("structLogs"); + Assert.assertTrue(structLogs.isArray()); + Assert.assertTrue(structLogs.size() > 0); + + // Check Filters + + Assert.assertNotEquals(witnessResult, result); + Assert.assertFalse(witnessStructLogs.findValues("stack").isEmpty()); + Assert.assertFalse(witnessStructLogs.findValues("memory").isEmpty()); + Assert.assertFalse(witnessStructLogs.findValues("storage").isEmpty()); + Assert.assertTrue(structLogs.findValues("stack").isEmpty()); + Assert.assertTrue(structLogs.findValues("memory").isEmpty()); + Assert.assertTrue(structLogs.findValues("storage").isEmpty()); + } } diff --git a/rskj-core/src/test/java/co/rsk/rpc/modules/debug/TraceOptionsTest.java b/rskj-core/src/test/java/co/rsk/rpc/modules/debug/TraceOptionsTest.java index ad8b857d205..1cf81fb380c 100644 --- a/rskj-core/src/test/java/co/rsk/rpc/modules/debug/TraceOptionsTest.java +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/debug/TraceOptionsTest.java @@ -22,6 +22,7 @@ import org.junit.Assert; import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -91,6 +92,16 @@ public void testTraceOptions_nullTraceOptionsGiven_disabledFieldsAndUnsupportedO Assert.assertEquals(0, options.getUnsupportedOptions().size()); } + @Test + public void testTraceOptions_emptyTraceOptionsGiven_disabledFieldsAndUnsupportedOptionsShouldReturnEmptySet() { + // When + TraceOptions options = new TraceOptions(Collections.emptyMap()); + + // Then + Assert.assertEquals(0, options.getDisabledFields().size()); + Assert.assertEquals(0, options.getUnsupportedOptions().size()); + } + @Test public void testTraceOptions_unsupportedOptionsGiven_unsupportedOptionsShouldReturnAllOfThem() { // Given