Skip to content

Instantly share code, notes, and snippets.

@dimchansky
Last active January 29, 2018 15:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dimchansky/64d296080a92718c65b4423dcfb4a311 to your computer and use it in GitHub Desktop.
Save dimchansky/64d296080a92718c65b4423dcfb4a311 to your computer and use it in GitHub Desktop.
Converts PARITY trace JSON to Graphviz DOT file
package main
import (
"encoding/json"
"fmt"
"io"
"math/big"
"os"
"strconv"
"strings"
)
type traceStep struct {
Action struct {
CallType string `json:"callType"`
From string `json:"from"`
Gas string `json:"gas"`
Input string `json:"input"`
To string `json:"to"`
Value string `json:"value"`
} `json:"action"`
BlockHash string `json:"blockHash"`
BlockNumber int `json:"blockNumber"`
Result struct {
GasUsed string `json:"gasUsed"`
Output string `json:"output"`
} `json:"result"`
Subtraces int `json:"subtraces"`
TraceAddress []interface{} `json:"traceAddress"`
TransactionHash string `json:"transactionHash"`
TransactionPosition int `json:"transactionPosition"`
Type string `json:"type"`
}
type trace []traceStep
func main() {
tr, err := loadTrace()
if err != nil {
fmt.Printf("Failed to load trace from JSON: %v\n", err)
return
}
// collect unique addresses
uniqueAddresses := make(map[string]struct{})
for _, ts := range tr {
uniqueAddresses[ts.Action.From] = struct{}{}
uniqueAddresses[ts.Action.To] = struct{}{}
}
// output graph
fmt.Println("digraph G {")
for addr, _ := range uniqueAddresses {
quotAddr := strconv.Quote(addr)
fmt.Printf("\t%v [label=%v];\n", quotAddr, quotAddr)
}
fmt.Println()
for _, ts := range tr {
fmt.Printf("\t%v -> %v [label=\"%v: %v\\nvalue: %v ETH\\ngas: %v\\ngas used: %v\"];\n",
strconv.Quote(ts.Action.From), strconv.Quote(ts.Action.To),
ts.Action.CallType, truncateInputToCall(ts.Action.Input),
hexStrToETH(ts.Action.Value),
hexStrToBigInt(ts.Action.Gas),
hexStrToBigInt(ts.Result.GasUsed))
}
fmt.Println("}")
}
var ether = big.NewInt(1000000000000000000) // 1 ether in wei
func truncateInputToCall(s string) string {
if len(s) > 10 {
return s[:10]
}
return s
}
func hexStrToETH(s string) float64 {
bi := hexStrToBigInt(s)
r := new(big.Rat).SetFrac(bi, ether)
eth, _ := r.Float64()
return eth
}
func hexStrToBigInt(s string) *big.Int {
bi, _ := new(big.Int).SetString(strings.TrimPrefix(s, "0x"), 16)
return bi
}
func loadTrace() (trace, error) {
if len(os.Args) == 2 {
return loadTraceFromFile(os.Args[1])
}
return loadTraceFromReader(os.Stdin)
}
func loadTraceFromFile(file string) (trace, error) {
f, err := os.Open(file)
if err != nil {
return nil, fmt.Errorf("os.Open: %v", err)
}
defer f.Close()
return loadTraceFromReader(f)
}
func loadTraceFromReader(f io.Reader) (trace, error) {
jsonParser := json.NewDecoder(f)
var tr trace
if err := jsonParser.Decode(&tr); err != nil {
return nil, fmt.Errorf("jsonParser.Decode: %v", err)
}
return tr, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment