From f5359a3a8416a2680911221ae1ad8e48b5d96da8 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Fri, 29 Oct 2021 19:23:35 -0300 Subject: [PATCH 1/2] Adding trace options --- .../rpc/modules/debug/DebugModuleImpl.java | 24 ++-- .../rsk/rpc/modules/debug/TraceOptions.java | 65 +++++++++ .../crypto/signature/Secp256k1ServiceBC.java | 1 + .../signature/Secp256k1ServiceNative.java | 1 + .../main/java/org/ethereum/vm/trace/Op.java | 2 + .../vm/trace/ProgramTraceProcessor.java | 31 +++- .../rpc/modules/debug/TraceOptionsTest.java | 132 ++++++++++++++++++ .../signature/Secp256k1ServiceTest.java | 2 +- 8 files changed, 243 insertions(+), 15 deletions(-) create mode 100644 rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java create mode 100644 rskj-core/src/test/java/co/rsk/rpc/modules/debug/TraceOptionsTest.java 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 950ebcc828f..88bce9f9f6c 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 @@ -68,9 +68,13 @@ public String wireProtocolQueueSize() { public JsonNode traceTransaction(String transactionHash, Map traceOptions) { logger.trace("debug_traceTransaction({}, {})", transactionHash, traceOptions); - if (traceOptions != null && !traceOptions.isEmpty()) { - // TODO: implement the logic that takes into account trace options. - logger.warn("Received {} trace options. For now trace options are being ignored", traceOptions); + TraceOptions options = new TraceOptions(traceOptions); + + if (options.getUnsupportedOptions().size() > 0) { + // TODO: implement the logic that takes into account the remaining trace options. + logger.warn( + "Received {} unsupported trace options.", + options.getUnsupportedOptions()); } byte[] hash = stringHexToByteArray(transactionHash); @@ -86,7 +90,7 @@ public JsonNode traceTransaction(String transactionHash, Map tra Transaction tx = block.getTransactionsList().get(txInfo.getIndex()); txInfo.setTransaction(tx); - ProgramTraceProcessor programTraceProcessor = new ProgramTraceProcessor(); + ProgramTraceProcessor programTraceProcessor = new ProgramTraceProcessor(options); blockExecutor.traceBlock(programTraceProcessor, 0, block, parent.getHeader(), false, false); return programTraceProcessor.getProgramTraceAsJsonNode(tx.getHash()); @@ -96,9 +100,13 @@ public JsonNode traceTransaction(String transactionHash, Map tra public JsonNode traceBlock(String blockHash, Map traceOptions) { logger.trace("debug_traceBlockByHash({}, {})", blockHash, traceOptions); - if (traceOptions != null && !traceOptions.isEmpty()) { - // TODO: implement the logic that takes into account trace options. - logger.warn("Received {} trace options. For now trace options are being ignored", traceOptions); + TraceOptions options = new TraceOptions(traceOptions); + + if (options.getUnsupportedOptions().size() > 0) { + // TODO: implement the logic that takes into account the remaining trace options. + logger.warn( + "Received {} unsupported trace options.", + options.getUnsupportedOptions()); } byte[] bHash = stringHexToByteArray(blockHash); @@ -110,7 +118,7 @@ public JsonNode traceBlock(String blockHash, Map traceOptions) { Block parent = blockStore.getBlockByHash(block.getParentHash().getBytes()); - ProgramTraceProcessor programTraceProcessor = new ProgramTraceProcessor(); + ProgramTraceProcessor programTraceProcessor = new ProgramTraceProcessor(options); blockExecutor.traceBlock(programTraceProcessor, 0, block, parent.getHeader(), false, false); List txHashes = block.getTransactionsList().stream() 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 new file mode 100644 index 00000000000..e24b2c5ada6 --- /dev/null +++ b/rskj-core/src/main/java/co/rsk/rpc/modules/debug/TraceOptions.java @@ -0,0 +1,65 @@ +/* + * 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; + +import java.util.*; + +public class TraceOptions { + + private Set disabledFields; + private Set unsupportedOptions; + + private TraceOptions() {} + + 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"); + } + unsupportedOptions = traceOptions.keySet(); + } + } + + public Set getDisabledFields() { + return disabledFields; + } + + public Set getUnsupportedOptions() { + return unsupportedOptions; + } + +} diff --git a/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceBC.java b/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceBC.java index dbb129d721f..3368a2dce25 100644 --- a/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceBC.java +++ b/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceBC.java @@ -65,6 +65,7 @@ class Secp256k1ServiceBC implements Secp256k1Service { @Override public ECKey recoverFromSignature(int recId, ECDSASignature sig, byte[] messageHash, boolean compressed) { check(recId >= 0, "recId must be positive"); + check(recId <= 3, "recId must be less than or equal to 3"); check(sig.getR().signum() >= 0, "r must be positive"); check(sig.getS().signum() >= 0, "s must be positive"); check(messageHash != null, "messageHash must not be null"); diff --git a/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceNative.java b/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceNative.java index b8aeda40aa5..bf964579182 100644 --- a/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceNative.java +++ b/rskj-core/src/main/java/org/ethereum/crypto/signature/Secp256k1ServiceNative.java @@ -41,6 +41,7 @@ public class Secp256k1ServiceNative extends Secp256k1ServiceBC { @Override public ECKey recoverFromSignature(int recId, ECDSASignature sig, byte[] messageHash, boolean compressed) { check(recId >= 0, "recId must be positive"); + check(recId <= 3, "recId must be less than or equal to 3"); check(sig.getR().signum() >= 0, "r must be positive"); check(sig.getS().signum() >= 0, "s must be positive"); check(messageHash != null, "messageHash must not be null"); diff --git a/rskj-core/src/main/java/org/ethereum/vm/trace/Op.java b/rskj-core/src/main/java/org/ethereum/vm/trace/Op.java index 56dcb36a945..1a001f1de19 100644 --- a/rskj-core/src/main/java/org/ethereum/vm/trace/Op.java +++ b/rskj-core/src/main/java/org/ethereum/vm/trace/Op.java @@ -19,6 +19,7 @@ package org.ethereum.vm.trace; +import com.fasterxml.jackson.annotation.JsonFilter; import org.ethereum.util.ByteUtil; import org.ethereum.vm.OpCode; import org.ethereum.vm.program.Memory; @@ -29,6 +30,7 @@ import java.util.List; import java.util.Map; +@JsonFilter("opFilter") public class Op { private OpCode op; 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 4849381b0f2..4dda00066e0 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 @@ -20,14 +20,14 @@ package org.ethereum.vm.trace; import co.rsk.crypto.Keccak256; +import co.rsk.rpc.modules.debug.TraceOptions; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.stream.Collectors; /** @@ -39,6 +39,17 @@ public class ProgramTraceProcessor { private final Map traces = new HashMap<>(); + private TraceOptions traceOptions; + + public ProgramTraceProcessor() { + traceOptions = new TraceOptions(Collections.emptyMap()); + } + + public ProgramTraceProcessor(TraceOptions options) { + this(); + traceOptions = options; + } + public void processProgramTrace(ProgramTrace programTrace, Keccak256 txHash) { this.traces.put(txHash, programTrace); } @@ -53,7 +64,11 @@ public JsonNode getProgramTracesAsJsonNode(List txHashes) { .filter(Objects::nonNull) .collect(Collectors.toList()); - return OBJECT_MAPPER.valueToTree(txTraces); + SimpleFilterProvider filterProvider = new SimpleFilterProvider(); + filterProvider.addFilter("opFilter", + SimpleBeanPropertyFilter.serializeAllExcept(traceOptions.getDisabledFields())); + + return OBJECT_MAPPER.setFilterProvider(filterProvider).valueToTree(txTraces); } public JsonNode getProgramTraceAsJsonNode(Keccak256 txHash) { @@ -63,7 +78,11 @@ public JsonNode getProgramTraceAsJsonNode(Keccak256 txHash) { return null; } - return OBJECT_MAPPER.valueToTree(trace); + SimpleFilterProvider filterProvider = new SimpleFilterProvider(); + filterProvider.addFilter("opFilter", + SimpleBeanPropertyFilter.serializeAllExcept(traceOptions.getDisabledFields())); + + return makeObjectMapper().setFilterProvider(filterProvider).valueToTree(trace); } private static ObjectMapper makeObjectMapper() { 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 new file mode 100644 index 00000000000..ad8b857d205 --- /dev/null +++ b/rskj-core/src/test/java/co/rsk/rpc/modules/debug/TraceOptionsTest.java @@ -0,0 +1,132 @@ +/* + * 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; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class TraceOptionsTest { + + @Test + public void testTraceOptions_allFieldsSetAsDisabled_disabledFieldsShouldReturnAllFields() { + // Given + Map traceOptions = new HashMap<>(); + traceOptions.put("disableStorage", "true"); + traceOptions.put("disableMemory", "true"); + traceOptions.put("disableStack", "true"); + + // When + TraceOptions options = new TraceOptions(traceOptions); + + // Then + Assert.assertEquals(3, options.getDisabledFields().size()); + Assert.assertTrue(options.getDisabledFields().contains("storage")); + Assert.assertTrue(options.getDisabledFields().contains("memory")); + Assert.assertTrue(options.getDisabledFields().contains("stack")); + } + + @Test + public void testTraceOptions_anyFieldsSetAsDisabled_disabledFieldsShouldReturnEmptySet() { + // Given + Map traceOptions = new HashMap<>(); + traceOptions.put("disableStorages", "false"); + traceOptions.put("disablesMemory", "false"); + traceOptions.put("disableStack", "false"); + + // When + TraceOptions options = new TraceOptions(traceOptions); + + // Then + Assert.assertEquals(0, options.getDisabledFields().size()); + Assert.assertFalse(options.getDisabledFields().contains("storage")); + Assert.assertFalse(options.getDisabledFields().contains("memory")); + Assert.assertFalse(options.getDisabledFields().contains("stack")); + } + + @Test + public void testTraceOptions_someFieldsSetAsDisabled_disabledFieldsShouldReturnDisabledOnes() { + // Given + Map traceOptions = new HashMap<>(); + traceOptions.put("disableStorage", "true"); + traceOptions.put("disableMemory", "false"); + traceOptions.put("disableStack", "true"); + + // When + TraceOptions options = new TraceOptions(traceOptions); + + // Then + Assert.assertEquals(2, options.getDisabledFields().size()); + Assert.assertTrue(options.getDisabledFields().contains("storage")); + Assert.assertFalse(options.getDisabledFields().contains("memory")); + Assert.assertTrue(options.getDisabledFields().contains("stack")); + } + + @Test + public void testTraceOptions_nullTraceOptionsGiven_disabledFieldsAndUnsupportedOptionsShouldReturnEmptySet() { + // When + TraceOptions options = new TraceOptions(null); + + // Then + Assert.assertEquals(0, options.getDisabledFields().size()); + Assert.assertEquals(0, options.getUnsupportedOptions().size()); + } + + @Test + public void testTraceOptions_unsupportedOptionsGiven_unsupportedOptionsShouldReturnAllOfThem() { + // Given + Map traceOptions = new HashMap<>(); + traceOptions.put("unsupportedOption.1", "1"); + traceOptions.put("unsupportedOption.2", null); + + // When + TraceOptions options = new TraceOptions(traceOptions); + + // Then + Assert.assertEquals(2, options.getUnsupportedOptions().size()); + Assert.assertTrue(options.getUnsupportedOptions().contains("unsupportedOption.1")); + Assert.assertTrue(options.getUnsupportedOptions().contains("unsupportedOption.2")); + } + + @Test + public void testTraceOptions_mixOfSupportedAndUnsupportedOptionsGiven_disabledFieldsAndUnsupportedOptionsShouldReturnOK() { + // Given + Map traceOptions = new HashMap<>(); + traceOptions.put("disableMemory", "true"); + traceOptions.put("disableStorage", "True"); // True != true but should also work + traceOptions.put("unsupportedOption.1", "1"); + traceOptions.put("unsupportedOption.2", null); + + // When + TraceOptions options = new TraceOptions(traceOptions); + + // Then + Assert.assertEquals(2, options.getDisabledFields().size()); + Assert.assertTrue(options.getDisabledFields().contains("storage")); + Assert.assertTrue(options.getDisabledFields().contains("memory")); + + Assert.assertEquals(2, options.getUnsupportedOptions().size()); + Assert.assertTrue(options.getUnsupportedOptions().contains("unsupportedOption.1")); + Assert.assertTrue(options.getUnsupportedOptions().contains("unsupportedOption.2")); + } + +} diff --git a/rskj-core/src/test/java/org/ethereum/crypto/signature/Secp256k1ServiceTest.java b/rskj-core/src/test/java/org/ethereum/crypto/signature/Secp256k1ServiceTest.java index cc2987942c9..4b15ec9316e 100644 --- a/rskj-core/src/test/java/org/ethereum/crypto/signature/Secp256k1ServiceTest.java +++ b/rskj-core/src/test/java/org/ethereum/crypto/signature/Secp256k1ServiceTest.java @@ -222,7 +222,7 @@ public void testRecoverFromSignature_fixed_values_garbage() throws SignatureExce byte[] s = Arrays.concatenate(new byte[]{1}, ByteUtil.bigIntegerToBytes(this.s, 64)); byte[] r = Arrays.concatenate(new byte[]{1}, ByteUtil.bigIntegerToBytes(this.r, 64)); ECDSASignature signature = ECDSASignature.fromComponents(r, s, v); - ECKey key = this.getSecp256k1().recoverFromSignature(v, signature, messageHash, true); + ECKey key = this.getSecp256k1().recoverFromSignature(v - 27, signature, messageHash, true); assertNull(key); } From 61613123aeb23c3d50f69ee7e775f170f37e8dd6 Mon Sep 17 00:00:00 2001 From: Nazaret Garcia Date: Thu, 13 Jan 2022 18:54:17 -0300 Subject: [PATCH 2/2] Adding tests --- .../rpc/modules/debug/DebugModuleImpl.java | 4 +- .../rsk/rpc/modules/debug/DisableOption.java | 34 +++++++++ .../rsk/rpc/modules/debug/TraceOptions.java | 55 +++++++------- .../vm/trace/ProgramTraceProcessor.java | 9 +-- .../modules/debug/DebugModuleImplTest.java | 73 +++++++++++++++++-- .../rpc/modules/debug/TraceOptionsTest.java | 11 +++ 6 files changed, 145 insertions(+), 41 deletions(-) create mode 100644 rskj-core/src/main/java/co/rsk/rpc/modules/debug/DisableOption.java 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