Skip to content

Instantly share code, notes, and snippets.

@peekpi
Last active November 6, 2020 14:50
Show Gist options
  • Save peekpi/7dd11519b6df710f2423f797d1ace7e7 to your computer and use it in GitHub Desktop.
Save peekpi/7dd11519b6df710f2423f797d1ace7e7 to your computer and use it in GitHub Desktop.
try generate inter transactions with tracer module

Geth's tracer module embed 'Duktape' which is a Javascript engine, and use it do the complicated trace. And there is already a script to generate inner taransactions. Janet has ported the tracer module from eth to harmony, but he removed the js engine. So if we want to use the script, we need to port the tracer module with js engine, and make a few changes to the script to make it work as we expect it to.

pragma solidity ^0.6.2;
contract SubContract {
function _revert(bool cond, string memory error) public pure {
require(cond, error);
}
function destruct() public {}
function deposit() payable public {}
function transfer(address payable to) payable public {
to.transfer(msg.value);
}
}
contract Entry {
SubContract public subc;
constructor() public {
subc = create();
}
function create() private returns(SubContract) {
return (new SubContract());
}
function testTransfer() payable public {
SubContract test = SubContract(create());
test.transfer.value(msg.value/2)(msg.sender);
msg.sender.transfer(msg.value/2);
test._revert(msg.value&1 == 0, "only even");
}
}
const Entry = artifacts.require("Entry");
web3.extend({methods: [{name: 'traceTransaction',call: 'debug_traceTransaction', params: 2, inputFormatter: [null, null]}]})
const OpFuncs = {
CALL,
REVERT,
STOP,
CREATE,
RETURN,
STATICCALL,
DELEGATECALL,
CALLCODE,
CREATE2,
SELFDESTRUCT
}
async function main(){
const c = await Entry.new();
const sub = await c.subc();
console.log(`sub-constrct: ${sub}`);
const tx = await c.testTransfer({value:101});
await txReplay(tx.tx);
}
const step = `
function(log, db) {
if(log.op.toString() == "CALL")
this.data.push({
op: log.op.toString()
});
}
`
const short=(e,n)=>e.length>n?`${e.slice(0,n-3)}...`:e;
async function txReplay(txid) {
console.log('txReplay:', txid);
//const traceResult = await web3.traceTransaction(txid, {tracer: `{data: [], fault: function(log) {}, step:${step}, result: function() { return JSON.stringify(this.data); }}`});
const traceResult = await web3.traceTransaction(txid, {tracer: 'callTracer'});
console.log(traceResult);
return;
//const traceResult = await web3.traceTransaction(txid, {});
console.log(`returnValue: ${traceResult.returnValue}`);
console.log(`gas: ${traceResult.gas}`);
const logs = traceResult.structLogs;
const ops = logs.filter(log=>OpFuncs[log.op])
ops.forEach(ele => {
const func = OpFuncs[ele.op] || UNKNOW;
func(ele, ele.memory.slice(), ele.stack.slice());
});
}
function memRead(memory, offset, size) {
offsetAlign = offset.toString()/32;
sizeAlign = (size.toString()+31)/32;
data = memory.slice(offsetAlign, offsetAlign + sizeAlign);
data = data.reduce((a,b)=>a+b,'');
return data.slice(0, 2*size.toString());
}
function stackRead(stack, n) {
return stack.slice(-n).reverse().map(ele=>BigInt('0x'+ele));
}
function UNKNOW(op, memory, stack) {
console.log(`${' '.repeat(op.depth*2)}unknow op:${op.op}`)
}
function CALL(op, memory, stack) {
stack.pop();
[addr, value, inOffset, inSize, retOffset, retSize] = stackRead(stack, 6);
const callData = memRead(memory, inOffset, inSize);
console.log(`${' '.repeat(op.depth*2)}${op.op}: ${addr} value: ${value} data: ${callData}`)
}
function REVERT(op, memory, stack) {
[offset, size] = stackRead(stack, 2);
ret = memRead(memory, offset, size);
revertMsg = web3.eth.abi.decodeParameter('string', `0x${ret.slice(8)}`);
console.log(`${' '.repeat(op.depth*2)}${op.op}("${revertMsg}")`);
}
function STOP(op, memory, stack) {
console.log(`${' '.repeat(op.depth*2)}${op.op}`);
}
function CREATE(op, memory, stack) {
[value, offset, size] = stackRead(stack, 3);
input = memRead(memory, offset, size);
console.log(`${' '.repeat(op.depth*2)}${op.op} new contract ${value}`);
}
function CREATE2(op, memory, stack) {
[endowment, offset, size, salt] = stackRead(stack, 4);
input = memRead(memory, offset, size);
console.log(`${' '.repeat(op.depth*2)}${op.op} new contract ${salt} ${endowment}`);
}
function RETURN(op, memory, stack) {
[offset, size] = stackRead(stack, 2);
ret = memRead(memory, offset, size);
console.log(`${' '.repeat(op.depth*2)}${op.op} ${short(ret, 128)}`);
}
function STATICCALL(op, memory, stack) {
stack.pop();
[addr, inOffset, inSize, retOffset, retSize] = stackRead(stack, 5);
args = memRead(memory, inOffset, inSize);
console.log(`${' '.repeat(op.depth*2)}${op.op} ${addr} args: ${args}`);
}
function DELEGATECALL(op, memory, stack) {
stack.pop();
[addr, inOffset, inSize, retOffset, retSize] = stackRead(stack, 5);
args = memRead(memory, inOffset, inSize);
console.log(`${' '.repeat(op.depth*2)}${op.op} ${addr} args: ${args}`);
}
function CALLCODE(op, memory, stack) {
stack.pop();
[addr, value, inOffset, inSize, retOffset, retSize] = stackRead(stack, 6);
args = memRead(memory, inOffset, inSize);
console.log(`${' '.repeat(op.depth*2)}${op.op} ${addr} args: ${args}`);
}
function SELFDESTRUCT(op, memory, stack) {
[to] = stackRead(stack, 1);
console.log(`${' '.repeat(op.depth*2)}${op.op} ${to}`);
}
module.exports = function (result) {
return main()
.then(result).catch(result);
}
{
type: 'CALL',
from: '0x79a11897fe6243fe433dc5fe882acf8e1c8caf12',
to: '0x0d0207a709f5fd0e941595c8e2fce85b9e9692eb',
value: '0x65',
gas: '0x663e9f',
gasUsed: '0x2fda1',
input: '0xd591221f',
output: '0x08c379a0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000096f6e6c79206576656e0000000000000000000000000000000000000000000000',
error: 'execution reverted',
time: '9.267301ms',
calls: [
{
type: 'CREATE',
from: '0x0d0207a709f5fd0e941595c8e2fce85b9e9692eb',
to: '0xdb2e799325f57deeba8c51889d5387ce26ffc165',
value: '0x0',
gas: '0x642941',
gasUsed: '0x218a7',
input: '0x608060405234801561001057600080fd5b506102ae806100206000396000f3fe60806040526004361061003f5760003560e01c80631a695230146100445780632b68b9c614610088578063911374c81461009f578063d0e30db014610173575b600080fd5b6100866004803603602081101561005a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061017d565b005b34801561009457600080fd5b5061009d6101c7565b005b3480156100ab57600080fd5b50610171600480360360408110156100c257600080fd5b81019080803515159060200190929190803590602001906401000000008111156100eb57600080fd5b8201836020820111156100fd57600080fd5b8035906020019184600183028401116401000000008311171561011f57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506101c9565b005b61017b610276565b005b8073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f193505050501580156101c3573d6000803e3d6000fd5b5050565b565b818190610271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561023657808201518184015260208101905061021b565b50505050905090810190601f1680156102635780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050565b56fea2646970667358221220e72552806240a81657564c4b0254a97d5f4942ab93f949c6774c76b9d7bb89b764736f6c63430006020033',
output: '0x60806040526004361061003f5760003560e01c80631a695230146100445780632b68b9c614610088578063911374c81461009f578063d0e30db014610173575b600080fd5b6100866004803603602081101561005a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061017d565b005b34801561009457600080fd5b5061009d6101c7565b005b3480156100ab57600080fd5b50610171600480360360408110156100c257600080fd5b81019080803515159060200190929190803590602001906401000000008111156100eb57600080fd5b8201836020820111156100fd57600080fd5b8035906020019184600183028401116401000000008311171561011f57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192905050506101c9565b005b61017b610276565b005b8073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f193505050501580156101c3573d6000803e3d6000fd5b5050565b565b818190610271576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825283818151815260200191508051906020019080838360005b8381101561023657808201518184015260208101905061021b565b50505050905090810190601f1680156102635780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050565b56fea2646970667358221220e72552806240a81657564c4b0254a97d5f4942ab93f949c6774c76b9d7bb89b764736f6c63430006020033'
},
{
type: 'CALL',
from: '0x0d0207a709f5fd0e941595c8e2fce85b9e9692eb',
to: '0xdb2e799325f57deeba8c51889d5387ce26ffc165',
value: '0x32',
gas: '0x61f925',
gasUsed: '0x1e0b',
input: '0x1a69523000000000000000000000000079a11897fe6243fe433dc5fe882acf8e1c8caf12',
output: '0x',
calls: [Array]
},
{
type: 'CALL',
from: '0x0d0207a709f5fd0e941595c8e2fce85b9e9692eb',
to: '0x79a11897fe6243fe433dc5fe882acf8e1c8caf12',
value: '0x32',
input: '0x',
output: '0x'
},
{
type: 'STATICCALL',
from: '0x0d0207a709f5fd0e941595c8e2fce85b9e9692eb',
to: '0xdb2e799325f57deeba8c51889d5387ce26ffc165',
gas: '0x61b82b',
gasUsed: '0x40f',
input: '0x911374c80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000096f6e6c79206576656e0000000000000000000000000000000000000000000000',
error: 'execution reverted'
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment