Skip to content

Commit fcd83f4

Browse files
update tracer, fix update-tracer build rule (#86)
1 parent 56328e8 commit fcd83f4

File tree

2 files changed

+51
-194
lines changed

2 files changed

+51
-194
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ build-release:
3939
docker save rosetta-ethereum:$(version) | gzip > rosetta-ethereum-$(version).tar.gz;
4040

4141
update-tracer:
42-
curl https://raw.githubusercontent.com/ethereum/go-ethereum/master/eth/tracers/internal/tracers/call_tracer.js -o ethereum/client/call_tracer.js
42+
curl https://raw.githubusercontent.com/ethereum/go-ethereum/master/eth/tracers/js/internal/tracers/call_tracer_js.js -o ethereum/call_tracer.js
4343

4444
update-bootstrap-balances:
4545
go run main.go utils:generate-bootstrap ethereum/genesis_files/mainnet.json rosetta-cli-conf/mainnet/bootstrap_balances.json;

ethereum/call_tracer.js

Lines changed: 50 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2017 The go-ethereum Authors
1+
// Copyright 2021 The go-ethereum Authors
22
// This file is part of the go-ethereum library.
33
//
44
// The go-ethereum library is free software: you can redistribute it and/or modify
@@ -14,190 +14,14 @@
1414
// You should have received a copy of the GNU Lesser General Public License
1515
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
1616

17-
// callTracer is a full blown transaction tracer that extracts and reports all
18-
// the internal calls made by a transaction, along with any useful information.
17+
18+
// callFrameTracer uses the new call frame tracing methods to report useful information
19+
// about internal messages of a transaction.
1920
{
20-
// callstack is the current recursive call stack of the EVM execution.
2121
callstack: [{}],
22-
23-
// descended tracks whether we've just descended from an outer transaction into
24-
// an inner call.
25-
descended: false,
26-
27-
// step is invoked for every opcode that the VM executes.
28-
step: function(log, db) {
29-
// Capture any errors immediately
30-
var error = log.getError();
31-
if (error !== undefined) {
32-
this.fault(log, db);
33-
return;
34-
}
35-
// We only care about system opcodes, faster if we pre-check once
36-
var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
37-
if (syscall) {
38-
var op = log.op.toString();
39-
}
40-
// If a new contract is being created, add to the call stack
41-
if (syscall && (op == 'CREATE' || op == "CREATE2")) {
42-
var inOff = log.stack.peek(1).valueOf();
43-
var inEnd = inOff + log.stack.peek(2).valueOf();
44-
45-
// Assemble the internal call report and store for completion
46-
var call = {
47-
type: op,
48-
from: toHex(log.contract.getAddress()),
49-
input: toHex(log.memory.slice(inOff, inEnd)),
50-
gasIn: log.getGas(),
51-
gasCost: log.getCost(),
52-
value: '0x' + log.stack.peek(0).toString(16)
53-
};
54-
this.callstack.push(call);
55-
this.descended = true
56-
return;
57-
}
58-
// If a contract is being self destructed, gather that as a subcall too
59-
if (syscall && op == 'SELFDESTRUCT') {
60-
var left = this.callstack.length;
61-
if (this.callstack[left-1].calls === undefined) {
62-
this.callstack[left-1].calls = [];
63-
}
64-
this.callstack[left-1].calls.push({
65-
type: op,
66-
from: toHex(log.contract.getAddress()),
67-
to: toHex(toAddress(log.stack.peek(0).toString(16))),
68-
gasIn: log.getGas(),
69-
gasCost: log.getCost(),
70-
value: '0x' + db.getBalance(log.contract.getAddress()).toString(16)
71-
});
72-
return
73-
}
74-
// If a new method invocation is being done, add to the call stack
75-
if (syscall && (op == 'CALL' || op == 'CALLCODE' || op == 'DELEGATECALL' || op == 'STATICCALL')) {
76-
var to = toAddress(log.stack.peek(1).toString(16));
77-
78-
// We don't skip any pre-compile invocations unlike the official
79-
// geth tracer. This can silence meaningful transfers.
80-
// if (isPrecompiled(to)) {
81-
// return
82-
// }
83-
84-
var off = (op == 'DELEGATECALL' || op == 'STATICCALL' ? 0 : 1);
85-
86-
var inOff = log.stack.peek(2 + off).valueOf();
87-
var inEnd = inOff + log.stack.peek(3 + off).valueOf();
88-
89-
// Assemble the internal call report and store for completion
90-
var call = {
91-
type: op,
92-
from: toHex(log.contract.getAddress()),
93-
to: toHex(to),
94-
input: toHex(log.memory.slice(inOff, inEnd)),
95-
gasIn: log.getGas(),
96-
gasCost: log.getCost(),
97-
outOff: log.stack.peek(4 + off).valueOf(),
98-
outLen: log.stack.peek(5 + off).valueOf()
99-
};
100-
if (op != 'DELEGATECALL' && op != 'STATICCALL') {
101-
call.value = '0x' + log.stack.peek(2).toString(16);
102-
}
103-
this.callstack.push(call);
104-
this.descended = true
105-
return;
106-
}
107-
// If we've just descended into an inner call, retrieve it's true allowance. We
108-
// need to extract if from within the call as there may be funky gas dynamics
109-
// with regard to requested and actually given gas (2300 stipend, 63/64 rule).
110-
if (this.descended) {
111-
if (log.getDepth() >= this.callstack.length) {
112-
this.callstack[this.callstack.length - 1].gas = log.getGas();
113-
} else {
114-
// TODO(karalabe): The call was made to a plain account. We currently don't
115-
// have access to the true gas amount inside the call and so any amount will
116-
// mostly be wrong since it depends on a lot of input args. Skip gas for now.
117-
}
118-
this.descended = false;
119-
}
120-
// If an existing call is returning, pop off the call stack
121-
if (syscall && op == 'REVERT') {
122-
this.callstack[this.callstack.length - 1].error = "execution reverted";
123-
return;
124-
}
125-
if (log.getDepth() == this.callstack.length - 1) {
126-
// Pop off the last call and get the execution results
127-
var call = this.callstack.pop();
128-
129-
if (call.type == 'CREATE' || call.type == "CREATE2") {
130-
// If the call was a CREATE, retrieve the contract address and output code
131-
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
132-
delete call.gasIn; delete call.gasCost;
133-
134-
var ret = log.stack.peek(0);
135-
if (!ret.equals(0)) {
136-
call.to = toHex(toAddress(ret.toString(16)));
137-
call.output = toHex(db.getCode(toAddress(ret.toString(16))));
138-
} else if (call.error === undefined) {
139-
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
140-
}
141-
} else {
142-
// If the call was a contract call, retrieve the gas usage and output
143-
if (call.gas !== undefined) {
144-
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16);
145-
}
146-
var ret = log.stack.peek(0);
147-
if (!ret.equals(0)) {
148-
call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
149-
} else if (call.error === undefined) {
150-
call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
151-
}
152-
delete call.gasIn; delete call.gasCost;
153-
delete call.outOff; delete call.outLen;
154-
}
155-
if (call.gas !== undefined) {
156-
call.gas = '0x' + bigInt(call.gas).toString(16);
157-
}
158-
// Inject the call into the previous one
159-
var left = this.callstack.length;
160-
if (this.callstack[left-1].calls === undefined) {
161-
this.callstack[left-1].calls = [];
162-
}
163-
this.callstack[left-1].calls.push(call);
164-
}
165-
},
166-
167-
// fault is invoked when the actual execution of an opcode fails.
168-
fault: function(log, db) {
169-
// If the topmost call already reverted, don't handle the additional fault again
170-
if (this.callstack[this.callstack.length - 1].error !== undefined) {
171-
return;
172-
}
173-
// Pop off the just failed call
174-
var call = this.callstack.pop();
175-
call.error = log.getError();
176-
177-
// Consume all available gas and clean any leftovers
178-
if (call.gas !== undefined) {
179-
call.gas = '0x' + bigInt(call.gas).toString(16);
180-
call.gasUsed = call.gas
181-
}
182-
delete call.gasIn; delete call.gasCost;
183-
delete call.outOff; delete call.outLen;
184-
185-
// Flatten the failed call into its parent
186-
var left = this.callstack.length;
187-
if (left > 0) {
188-
if (this.callstack[left-1].calls === undefined) {
189-
this.callstack[left-1].calls = [];
190-
}
191-
this.callstack[left-1].calls.push(call);
192-
return;
193-
}
194-
// Last call failed too, leave it in the stack
195-
this.callstack.push(call);
196-
},
197-
198-
// result is invoked when all the opcodes have been iterated over and returns
199-
// the final result of the tracing.
22+
fault: function(log, db) {},
20023
result: function(ctx, db) {
24+
// Prepare outer message info
20125
var result = {
20226
type: ctx.type,
20327
from: toHex(ctx.from),
@@ -207,22 +31,55 @@
20731
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
20832
input: toHex(ctx.input),
20933
output: toHex(ctx.output),
210-
time: ctx.time,
211-
};
34+
}
21235
if (this.callstack[0].calls !== undefined) {
213-
result.calls = this.callstack[0].calls;
36+
result.calls = this.callstack[0].calls
21437
}
21538
if (this.callstack[0].error !== undefined) {
216-
result.error = this.callstack[0].error;
39+
result.error = this.callstack[0].error
21740
} else if (ctx.error !== undefined) {
218-
result.error = ctx.error;
41+
result.error = ctx.error
21942
}
22043
if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
221-
delete result.output;
44+
delete result.output
22245
}
223-
return this.finalize(result);
224-
},
22546

47+
return this.finalize(result)
48+
},
49+
enter: function(frame) {
50+
var call = {
51+
type: frame.getType(),
52+
from: toHex(frame.getFrom()),
53+
to: toHex(frame.getTo()),
54+
input: toHex(frame.getInput()),
55+
gas: '0x' + bigInt(frame.getGas()).toString('16'),
56+
}
57+
if (frame.getValue() !== undefined){
58+
call.value='0x' + bigInt(frame.getValue()).toString(16)
59+
}
60+
this.callstack.push(call)
61+
},
62+
exit: function(frameResult) {
63+
var len = this.callstack.length
64+
if (len > 1) {
65+
var call = this.callstack.pop()
66+
call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16')
67+
var error = frameResult.getError()
68+
if (error === undefined) {
69+
call.output = toHex(frameResult.getOutput())
70+
} else {
71+
call.error = error
72+
if (call.type === 'CREATE' || call.type === 'CREATE2') {
73+
delete call.to
74+
}
75+
}
76+
len -= 1
77+
if (this.callstack[len-1].calls === undefined) {
78+
this.callstack[len-1].calls = []
79+
}
80+
this.callstack[len-1].calls.push(call)
81+
}
82+
},
22683
// finalize recreates a call object using the final desired field oder for json
22784
// serialization. This is a nicety feature to pass meaningfully ordered results
22885
// to users who don't interpret it, just display it.
@@ -242,14 +99,14 @@
24299
}
243100
for (var key in sorted) {
244101
if (sorted[key] === undefined) {
245-
delete sorted[key];
102+
delete sorted[key]
246103
}
247104
}
248105
if (sorted.calls !== undefined) {
249106
for (var i=0; i<sorted.calls.length; i++) {
250-
sorted.calls[i] = this.finalize(sorted.calls[i]);
107+
sorted.calls[i] = this.finalize(sorted.calls[i])
251108
}
252109
}
253-
return sorted;
110+
return sorted
254111
}
255112
}

0 commit comments

Comments
 (0)