VM trace JSON serialization bugfix.
This commit is contained in:
parent
429d1025a2
commit
a23617afda
|
@ -240,14 +240,16 @@ public class TransactionExecutor {
|
||||||
this.receipt = receipt;
|
this.receipt = receipt;
|
||||||
return;
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
String traceAsJson = "{}";
|
if (CONFIG.vmTrace()) {
|
||||||
if (program != null) {
|
String traceAsJson = "{}";
|
||||||
ProgramTrace trace = program.getProgramTrace();
|
if (program != null) {
|
||||||
trace.setResult(result.getHReturn());
|
ProgramTrace trace = program.getProgramTrace();
|
||||||
trace.setError(result.getException());
|
trace.setResult(result.getHReturn());
|
||||||
traceAsJson = trace.getJsonString();
|
trace.setError(result.getException());
|
||||||
|
traceAsJson = trace.asJsonString();
|
||||||
|
}
|
||||||
|
listener.onVMTraceCreated(txHash, traceAsJson);
|
||||||
}
|
}
|
||||||
listener.onVMTraceCreated(txHash, traceAsJson);
|
|
||||||
}
|
}
|
||||||
trackTx.commit();
|
trackTx.commit();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -8,35 +8,21 @@ import org.ethereum.vm.MessageCall.MsgType;
|
||||||
import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
|
import org.ethereum.vm.PrecompiledContracts.PrecompiledContract;
|
||||||
import org.ethereum.vmtrace.Op;
|
import org.ethereum.vmtrace.Op;
|
||||||
import org.ethereum.vmtrace.ProgramTrace;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Hex;
|
import org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.*;
|
||||||
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 static org.ethereum.config.SystemProperties.CONFIG;
|
import static org.ethereum.config.SystemProperties.CONFIG;
|
||||||
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
|
||||||
|
import static org.springframework.util.StringUtils.isEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
|
@ -840,7 +826,7 @@ public class Program {
|
||||||
|
|
||||||
public void saveProgramTraceToFile(String fileName) {
|
public void saveProgramTraceToFile(String fileName) {
|
||||||
|
|
||||||
if (!CONFIG.vmTrace()) return;
|
if (!CONFIG.vmTrace() || isEmpty(CONFIG.vmTraceDir())) return;
|
||||||
|
|
||||||
String dir = CONFIG.databaseDir() + "/" + CONFIG.vmTraceDir() + "/";
|
String dir = CONFIG.databaseDir() + "/" + CONFIG.vmTraceDir() + "/";
|
||||||
|
|
||||||
|
@ -849,21 +835,12 @@ public class Program {
|
||||||
BufferedWriter bw = null;
|
BufferedWriter bw = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
dumpFile.getParentFile().mkdirs();
|
dumpFile.getParentFile().mkdirs();
|
||||||
dumpFile.createNewFile();
|
dumpFile.createNewFile();
|
||||||
|
|
||||||
fw = new FileWriter(dumpFile.getAbsoluteFile());
|
fw = new FileWriter(dumpFile.getAbsoluteFile());
|
||||||
bw = new BufferedWriter(fw);
|
bw = new BufferedWriter(fw);
|
||||||
|
bw.write(programTrace.asJsonString());
|
||||||
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);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error(e.getMessage(), e);
|
logger.error(e.getMessage(), e);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
|
@ -1,23 +1,13 @@
|
||||||
package org.ethereum.vmtrace;
|
package org.ethereum.vmtrace;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import org.ethereum.vm.DataWord;
|
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 org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import static org.ethereum.vmtrace.Serializers.*;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data to for one program step to save.
|
* Data to for one program step to save.
|
||||||
|
@ -35,16 +25,18 @@ import java.util.Stack;
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
* @since 28.10.2014
|
* @since 28.10.2014
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class Op {
|
public class Op {
|
||||||
|
|
||||||
|
@JsonSerialize(using = OpCodeSerializer.class)
|
||||||
private byte op;
|
private byte op;
|
||||||
private int deep;
|
private int deep;
|
||||||
private int pc;
|
private int pc;
|
||||||
|
@JsonSerialize(using = DataWordSerializer.class)
|
||||||
private DataWord gas;
|
private DataWord gas;
|
||||||
private Map<String, String> storage;
|
@JsonSerialize(using = ByteArraySerializer.class)
|
||||||
private byte[] memory;
|
private byte[] memory;
|
||||||
private List<String> stack;
|
private List<String> stack;
|
||||||
|
private Map<String, String> storage;
|
||||||
|
|
||||||
public void setOp(byte op) {
|
public void setOp(byte op) {
|
||||||
this.op = op;
|
this.op = op;
|
||||||
|
@ -89,19 +81,10 @@ public class Op {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
return asJsonString();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String asJsonString() {
|
||||||
|
return serializeFieldsOnly(this, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,14 @@
|
||||||
package org.ethereum.vmtrace;
|
package org.ethereum.vmtrace;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
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 org.spongycastle.util.encoders.Hex;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
|
import static org.ethereum.vmtrace.Serializers.serializeFieldsOnly;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Roman Mandeleil
|
* @author Roman Mandeleil
|
||||||
|
@ -24,8 +16,6 @@ import static java.lang.String.format;
|
||||||
*/
|
*/
|
||||||
public class ProgramTrace {
|
public class ProgramTrace {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("vmtrace");
|
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
private byte[] txHash;
|
private byte[] txHash;
|
||||||
private List<Op> ops = new ArrayList<>();
|
private List<Op> ops = new ArrayList<>();
|
||||||
|
@ -41,7 +31,7 @@ public class ProgramTrace {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setError(Exception error) {
|
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) {
|
public void addOp(Op op) {
|
||||||
|
@ -56,14 +46,7 @@ public class ProgramTrace {
|
||||||
this.ops.addAll(programTrace.ops);
|
this.ops.addAll(programTrace.ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getJsonString() {
|
public String asJsonString() {
|
||||||
try {
|
return serializeFieldsOnly(this, true);
|
||||||
return new ObjectMapper()
|
|
||||||
.enable(SerializationFeature.INDENT_OUTPUT)
|
|
||||||
.writeValueAsString(this);
|
|
||||||
} catch (JsonProcessingException e) {
|
|
||||||
LOGGER.error("JSON serialization error: ", e);
|
|
||||||
return "{}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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