Created
September 9, 2019 17:01
-
-
Save DanielVF/a18d51bc7244239ec0568bbc103a7cee to your computer and use it in GitHub Desktop.
Pretty print control flow of Etherium debug traces
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const fs = require('fs') | |
const chalk = require('chalk') | |
const trace = JSON.parse(fs.readFileSync('2019_08_09_mike_fail.json')) | |
const logs = trace.result.structLogs as TraceLog[] | |
interface TraceLog { | |
depth: number | |
error: string | |
gas: number, | |
gasCost: number, | |
memory: string[], | |
op: string, | |
pc: number, | |
stack: string[], | |
storage: Record<string, string> | |
address: string | |
targetAddress: string, | |
callDepth: number | |
} | |
function targetAddress(log: TraceLog) : string { | |
return log.stack[log.stack.length - 2].replace(/^000000000000000000000000/, '') | |
} | |
function callArgs(log: TraceLog) : string { | |
if(log.op == "DELEGATECALL"){ | |
const start = parseInt(log.stack[log.stack.length - 3], 16) | |
const length = parseInt(log.stack[log.stack.length - 4], 16) | |
return getMemory(log, start, length) | |
} else { | |
const start = parseInt(log.stack[log.stack.length - 4], 16) | |
const length = parseInt(log.stack[log.stack.length - 5], 16) | |
return getMemory(log, start, length) | |
} | |
} | |
function getMemory(log: TraceLog, start: number, length: number): string { | |
let out : string[] = [] | |
let remaining = length | |
let slot = Math.floor(start / 32) | |
let offset = start - slot * 32 | |
// console.log('READ', start, length, remaining, slot, offset) | |
while(remaining > 0){ | |
const slotLen = Math.min(32 - offset, remaining) | |
remaining -= slotLen | |
// console.log('Slot',slot, offset, 'To read', slotLen, remaining) | |
if(log.memory[slot] == undefined){ | |
console.log("READ PAST SLOT") | |
break | |
} | |
out.push(log.memory[slot].substr(offset * 2, slotLen * 2)) | |
offset = 0 | |
slot += 1 | |
} | |
return out.join("") | |
} | |
// Build contract address flow | |
let currentAddress: string = 'START' | |
const contractAddressStack: string[] = [currentAddress] | |
logs.forEach(log => { | |
log.address = currentAddress | |
log.callDepth = contractAddressStack.length | |
if (log.op == "CREATE" || log.op == "CREATE2" ) { | |
currentAddress = 'NEW' | |
contractAddressStack.push(currentAddress) | |
} | |
if (log.op == "DELEGATECALL" || log.op == "STATICCALL" || log.op == "CALL" || log.op == "CALLCODE") { | |
currentAddress = targetAddress(log) | |
contractAddressStack.push(currentAddress) | |
} | |
if (log.op == "RETURN" || log.op == "REVERT") { | |
currentAddress = contractAddressStack.pop() as string | |
} | |
if (log.op == "CALL" || log.op == "CALLCODE" || log.op == "RETURN" || log.op == "DELEGATECALL" || log.op == "STATICCALL" || log.op == "REVERT") { | |
// console.log(log) | |
} | |
// console.log(log.pc, log.op) | |
}) | |
// console.log(logs[0]) | |
logs.forEach(log => { | |
if (log.op == "DELEGATECALL" || log.op == "STATICCALL" || log.op == "CALL" || log.op == "CALLCODE") { | |
const target = targetAddress(log) | |
const args = callArgs(log) | |
let methodSig = "?" | |
if(args.length > 0){ | |
methodSig = args.substr(0,8) | |
} | |
console.log(chalk.red(log.op), target, methodSig) | |
} | |
if (log.op == "RETURN" || log.op == "REVERT") { | |
console.log(chalk.red(log.op) ) | |
} | |
console.log(`${log.pc}\t${log.op} ${chalk.blue(log.gas)}`) | |
}) | |
console.log('----') | |
console.log(contractAddressStack) | |
logs.forEach(log => { | |
if (log.op == "CREATE" || log.op == "CREATE2"){ | |
console.log(`${chalk.red(log.op)} ${chalk.blue(log.gas)}` ) | |
} | |
const openingSpace = ' '.repeat(log.callDepth) | |
if (log.op == "DELEGATECALL" || log.op == "STATICCALL" || log.op == "CALL" || log.op == "CALLCODE") { | |
const target = targetAddress(log) | |
const args = callArgs(log) | |
let methodSig = "?" | |
if(args.length > 0){ | |
methodSig = args.substr(0,8) | |
} | |
console.log(`${openingSpace}${chalk.red(log.op)} ${target} ${methodSig} ${chalk.blue(log.gas)}\n${openingSpace}${chalk.grey(args)}`) | |
} | |
if (log.op == "RETURN" || log.op == "REVERT") { | |
console.log(`${openingSpace}${chalk.red(log.op)} ${chalk.blue(log.gas)}` ) | |
} | |
//console.log(log) | |
}) | |
// curl 'https://www.4byte.directory/api/v1/signatures/?hex_signature=095ea7b3' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment