VM trace JSON serialization bugfix.
This commit is contained in:
parent
429d1025a2
commit
a23617afda
|
@ -240,14 +240,16 @@ public class TransactionExecutor {
|
|||
this.receipt = receipt;
|
||||
return;
|
||||
} finally {
|
||||
String traceAsJson = "{}";
|
||||
if (program != null) {
|
||||
ProgramTrace trace = program.getProgramTrace();
|
||||
trace.setResult(result.getHReturn());
|
||||
trace.setError(result.getException());
|
||||
traceAsJson = trace.getJsonString();
|
||||
if (CONFIG.vmTrace()) {
|
||||
String traceAsJson = "{}";
|
||||
if (program != null) {
|
||||
ProgramTrace trace = program.getProgramTrace();
|
||||
trace.setResult(result.getHReturn());
|
||||
trace.setError(result.getException());
|
||||
traceAsJson = trace.asJsonString();
|
||||
}
|
||||
listener.onVMTraceCreated(txHash, traceAsJson);
|
||||
}
|
||||
listener.onVMTraceCreated(txHash, traceAsJson);
|
||||
}
|
||||
trackTx.commit();
|
||||
} else {
|
||||
|
|
|
@ -8,35 +8,21 @@ import org.ethereum.vm.MessageCall.MsgType;
|
|||
import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
|
||||
import org.ethereum.vmtrace.Op;
|
||||
import org.ethereum.vmtrace.ProgramTrace;
|
||||
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.codehaus.jackson.map.SerializationConfig;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.math.BigInteger;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.*;
|
||||
|
||||
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
||||
import static org.springframework.util.StringUtils.isEmpty;
|
||||
|
||||
/**
|
||||
* @author Roman Mandeleil
|
||||
|
@ -840,7 +826,7 @@ public class Program {
|
|||
|
||||
public void saveProgramTraceToFile(String fileName) {
|
||||
|
||||
if (!CONFIG.vmTrace()) return;
|
||||
if (!CONFIG.vmTrace() || isEmpty(CONFIG.vmTraceDir())) return;
|
||||
|
||||
String dir = CONFIG.databaseDir() + "/" + CONFIG.vmTraceDir() + "/";
|
||||
|
||||
|
@ -849,21 +835,12 @@ public class Program {
|
|||
BufferedWriter bw = null;
|
||||
|
||||
try {
|
||||
|
||||
dumpFile.getParentFile().mkdirs();
|
||||
dumpFile.createNewFile();
|
||||
|
||||
fw = new FileWriter(dumpFile.getAbsoluteFile());
|
||||
bw = new BufferedWriter(fw);
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
|
||||
|
||||
String originalJson = programTrace.getJsonString();
|
||||
JsonNode tree = objectMapper.readTree(originalJson);
|
||||
String formattedJson = objectMapper.writeValueAsString(tree);
|
||||
bw.write(formattedJson);
|
||||
|
||||
bw.write(programTrace.asJsonString());
|
||||
} catch (IOException e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} finally {
|
||||
|
|
|
@ -1,23 +1,13 @@
|
|||
package org.ethereum.vmtrace;
|
||||
|
||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.ethereum.vm.OpCode;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import static org.ethereum.vmtrace.Serializers.*;
|
||||
|
||||
/**
|
||||
* Data to for one program step to save.
|
||||
|
@ -35,16 +25,18 @@ import java.util.Stack;
|
|||
* @author Roman Mandeleil
|
||||
* @since 28.10.2014
|
||||
*/
|
||||
|
||||
public class Op {
|
||||
|
||||
@JsonSerialize(using = OpCodeSerializer.class)
|
||||
private byte op;
|
||||
private int deep;
|
||||
private int pc;
|
||||
@JsonSerialize(using = DataWordSerializer.class)
|
||||
private DataWord gas;
|
||||
private Map<String, String> storage;
|
||||
@JsonSerialize(using = ByteArraySerializer.class)
|
||||
private byte[] memory;
|
||||
private List<String> stack;
|
||||
private Map<String, String> storage;
|
||||
|
||||
public void setOp(byte op) {
|
||||
this.op = op;
|
||||
|
@ -89,19 +81,10 @@ public class Op {
|
|||
}
|
||||
|
||||
public String toString() {
|
||||
|
||||
Map<Object, Object> jsonData = new LinkedHashMap<>();
|
||||
|
||||
jsonData.put("op", OpCode.code(op).name());
|
||||
jsonData.put("deep", Long.toString(deep));
|
||||
jsonData.put("pc", Long.toString(pc));
|
||||
jsonData.put("gas", gas.value().toString());
|
||||
jsonData.put("stack", stack);
|
||||
jsonData.put("memory", memory == null ? "" : Hex.toHexString(memory));
|
||||
jsonData.put("storage", new JSONObject(storage));
|
||||
|
||||
return JSONValue.toJSONString(jsonData);
|
||||
return asJsonString();
|
||||
}
|
||||
|
||||
|
||||
private String asJsonString() {
|
||||
return serializeFieldsOnly(this, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
package org.ethereum.vmtrace;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static org.ethereum.vmtrace.Serializers.serializeFieldsOnly;
|
||||
|
||||
/**
|
||||
* @author Roman Mandeleil
|
||||
|
@ -24,8 +16,6 @@ import static java.lang.String.format;
|
|||
*/
|
||||
public class ProgramTrace {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("vmtrace");
|
||||
|
||||
@JsonIgnore
|
||||
private byte[] txHash;
|
||||
private List<Op> ops = new ArrayList<>();
|
||||
|
@ -41,7 +31,7 @@ public class ProgramTrace {
|
|||
}
|
||||
|
||||
public void setError(Exception error) {
|
||||
this.error = (error == null) ? "" : format("%s: %s", error.getClass(), error.getMessage());;
|
||||
this.error = (error == null) ? "" : format("%s: %s", error.getClass(), error.getMessage());
|
||||
}
|
||||
|
||||
public void addOp(Op op) {
|
||||
|
@ -56,14 +46,7 @@ public class ProgramTrace {
|
|||
this.ops.addAll(programTrace.ops);
|
||||
}
|
||||
|
||||
public String getJsonString() {
|
||||
try {
|
||||
return new ObjectMapper()
|
||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
||||
.writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
LOGGER.error("JSON serialization error: ", e);
|
||||
return "{}";
|
||||
}
|
||||
public String asJsonString() {
|
||||
return serializeFieldsOnly(this, true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
package org.ethereum.vmtrace;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import com.fasterxml.jackson.databind.introspect.VisibilityChecker;
|
||||
import org.ethereum.vm.DataWord;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
final class Serializers {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("vmtrace");
|
||||
|
||||
public static class DataWordSerializer extends JsonSerializer<DataWord> {
|
||||
|
||||
@Override
|
||||
public void serialize(DataWord gas, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
|
||||
jgen.writeString(gas.value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static class ByteArraySerializer extends JsonSerializer<byte[]> {
|
||||
|
||||
@Override
|
||||
public void serialize(byte[] memory, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
|
||||
jgen.writeString(Hex.toHexString(memory));
|
||||
}
|
||||
}
|
||||
|
||||
public static class OpCodeSerializer extends JsonSerializer<Byte> {
|
||||
|
||||
@Override
|
||||
public void serialize(Byte op, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
|
||||
jgen.writeString(org.ethereum.vm.OpCode.code(op).name());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String serializeFieldsOnly(Object value, boolean pretty) {
|
||||
try {
|
||||
ObjectMapper mapper = createMapper(pretty);
|
||||
mapper.setVisibilityChecker(fieldsOnlyVisibilityChecker(mapper));
|
||||
|
||||
return mapper.writeValueAsString(value);
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("JSON serialization error: ", e);
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
private static VisibilityChecker<?> fieldsOnlyVisibilityChecker(ObjectMapper mapper) {
|
||||
return mapper.getSerializationConfig().getDefaultVisibilityChecker()
|
||||
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
|
||||
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
|
||||
.withIsGetterVisibility(JsonAutoDetect.Visibility.NONE);
|
||||
}
|
||||
|
||||
public static ObjectMapper createMapper(boolean pretty) {
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
if (pretty) {
|
||||
mapper.enable(SerializationFeature.INDENT_OUTPUT);
|
||||
}
|
||||
return mapper;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue