Skip to content
This repository was archived by the owner on Oct 20, 2024. It is now read-only.

Commit 4edbbc8

Browse files
authored
Fix: NUMBER delimiter and opcode count error in tracing (#56)
1 parent 545c506 commit 4edbbc8

File tree

3 files changed

+96
-57
lines changed

3 files changed

+96
-57
lines changed

pkg/entrypoint/simulation.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ var (
2323
dummyPk, _ = crypto.GenerateKey()
2424

2525
// Up to the first number marker represents factory validation.
26-
factoryNumberLevel = "0"
26+
factoryNumberLevel = 0
2727

2828
// After the first number marker and before the second represents account validation.
29-
accountNumberLevel = "1"
29+
accountNumberLevel = 1
3030

3131
// After the second number marker represents paymaster validation.
32-
paymasterNumberLevel = "2"
32+
paymasterNumberLevel = 2
3333

3434
// Only one create2 opcode is allowed if these two conditions are met:
3535
// 1. op.initcode.length != 0

pkg/tracer/BundlerCollectorTracer.js

Lines changed: 88 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,112 @@
11
// This is the same BundlerCollectorTracer from github.com/eth-infinitism/bundler transpiled down to ES5.
22

33
var tracer = {
4-
numberLevels: {},
4+
numberLevels: [],
55
currentLevel: null,
66
keccak: [],
77
calls: [],
88
logs: [],
99
debug: [],
1010
lastOp: "",
1111
numberCounter: 0,
12-
count: 0,
1312

14-
fault: function (log, db) {
15-
this.debug.push(["fault", log.getError()]);
13+
fault: function fault(log, db) {
14+
this.debug.push(
15+
"fault depth=" +
16+
log.getDepth() +
17+
" gas=" +
18+
log.getGas() +
19+
" cost=" +
20+
log.getCost() +
21+
" err=" +
22+
log.getError()
23+
);
1624
},
17-
18-
result: function (ctx, db) {
25+
result: function result(ctx, db) {
1926
return {
2027
numberLevels: this.numberLevels,
2128
keccak: this.keccak,
2229
logs: this.logs,
2330
calls: this.calls,
24-
debug: this.debug,
31+
debug: this.debug, // for internal debugging.
2532
};
2633
},
27-
28-
enter: function (frame) {
29-
this.debug.push([
30-
"enter " +
34+
enter: function enter(frame) {
35+
this.debug.push(
36+
"enter gas=" +
37+
frame.getGas() +
38+
" type=" +
3139
frame.getType() +
32-
" " +
40+
" to=" +
3341
toHex(frame.getTo()) +
34-
" " +
35-
toHex(frame.getInput()).slice(0, 100),
36-
]);
37-
42+
" in=" +
43+
toHex(frame.getInput()).slice(0, 500)
44+
);
3845
this.calls.push({
3946
type: frame.getType(),
4047
from: toHex(frame.getFrom()),
4148
to: toHex(frame.getTo()),
49+
method: toHex(frame.getInput()).slice(0, 10),
50+
gas: frame.getGas(),
4251
value: frame.getValue(),
4352
});
4453
},
45-
46-
exit: function (frame) {
47-
this.debug.push(
48-
"exit err=" + frame.getError() + ", gas=" + frame.getGasUsed()
49-
);
54+
exit: function exit(frame) {
55+
this.calls.push({
56+
type: frame.getError() != null ? "REVERT" : "RETURN",
57+
gasUsed: frame.getGasUsed(),
58+
data: toHex(frame.getOutput()).slice(0, 1000),
59+
});
5060
},
5161

62+
// increment the "key" in the list. if the key is not defined yet, then set it to "1"
5263
countSlot: function countSlot(list, key) {
53-
list[key] += typeof list[key] == "number" ? 0 : 1;
64+
if (!list[key]) list[key] = 0;
65+
list[key] += 1;
5466
},
55-
5667
step: function step(log, db) {
5768
var opcode = log.op.toString();
69+
// this.debug.push(this.lastOp + '-' + opcode + '-' + log.getDepth() + '-' + log.getGas() + '-' + log.getCost())
70+
if (log.getGas() < log.getCost()) {
71+
this.currentLevel.oog = true;
72+
}
5873

59-
if (opcode === "NUMBER") this.numberCounter++;
60-
61-
if (this.numberLevels[this.numberCounter] == null) {
62-
this.currentLevel = this.numberLevels[this.numberCounter] = {
63-
access: {},
64-
opcodes: {},
65-
};
74+
if (opcode === "REVERT" || opcode === "RETURN") {
75+
if (log.getDepth() === 1) {
76+
// exit() is not called on top-level return/revert, so we reconstruct it
77+
// from opcode
78+
var ofs = parseInt(log.stack.peek(0).toString());
79+
var len = parseInt(log.stack.peek(1).toString());
80+
var data = toHex(log.memory.slice(ofs, ofs + len)).slice(0, 1000);
81+
this.debug.push(opcode + " " + data);
82+
this.calls.push({
83+
type: opcode,
84+
gasUsed: 0,
85+
data: data,
86+
});
87+
}
6688
}
6789

6890
if (log.getDepth() === 1) {
91+
// NUMBER opcode at top level split levels
92+
if (opcode === "NUMBER") this.numberCounter++;
93+
if (this.numberLevels[this.numberCounter] == null) {
94+
this.currentLevel = this.numberLevels[this.numberCounter] = {
95+
access: {},
96+
opcodes: {},
97+
contractSize: {},
98+
};
99+
}
100+
this.lastOp = "";
69101
return;
70102
}
71103

72104
if (this.lastOp === "GAS" && !opcode.includes("CALL")) {
105+
// count "GAS" opcode only if not followed by "CALL"
73106
this.countSlot(this.currentLevel.opcodes, "GAS");
74107
}
75-
76108
if (opcode !== "GAS") {
109+
// ignore "unimportant" opcodes:
77110
if (
78111
opcode.match(
79112
/^(DUP\d+|PUSH\d+|SWAP\d+|POP|ADD|SUB|MUL|DIV|EQ|LTE?|S?GTE?|SLT|SH[LR]|AND|OR|NOT|ISZERO)$/
@@ -82,51 +115,57 @@ var tracer = {
82115
this.countSlot(this.currentLevel.opcodes, opcode);
83116
}
84117
}
118+
if (
119+
opcode.match(/^(EXT.*|CALL|CALLCODE|DELEGATECALL|STATICCALL|CREATE2)$/) !=
120+
null
121+
) {
122+
// this.debug.push('op=' + opcode + ' last=' + this.lastOp + ' stacksize=' + log.stack.length())
123+
var idx = opcode.startsWith("EXT") ? 0 : 1;
124+
var addr = toAddress(log.stack.peek(idx).toString(16));
125+
var addrHex = toHex(addr);
126+
if (this.currentLevel.contractSize[addrHex] == null) {
127+
this.currentLevel.contractSize[addrHex] = db.getCode(addr).length;
128+
}
129+
}
85130

86131
this.lastOp = opcode;
87132

88133
if (opcode === "SLOAD" || opcode === "SSTORE") {
89134
var slot = log.stack.peek(0).toString(16);
90-
var addr = toHex(log.contract.getAddress());
135+
var _addr = toHex(log.contract.getAddress());
91136
var access = void 0;
92-
93-
if ((access = this.currentLevel.access[addr]) == null) {
94-
this.currentLevel.access[addr] = access = {
137+
if ((access = this.currentLevel.access[_addr]) == null) {
138+
this.currentLevel.access[_addr] = access = {
95139
reads: {},
96140
writes: {},
97141
};
98142
}
99-
100143
this.countSlot(opcode === "SLOAD" ? access.reads : access.writes, slot);
101144
}
102145

103-
if (opcode === "REVERT" || opcode === "RETURN") {
104-
var ofs = parseInt(log.stack.peek(0).toString());
105-
var len = parseInt(log.stack.peek(1).toString());
106-
107-
this.debug.push(
108-
opcode + " " + toHex(log.memory.slice(ofs, ofs + len)).slice(0, 100)
109-
);
110-
} else if (opcode === "KECCAK256") {
146+
if (opcode === "KECCAK256") {
147+
// collect keccak on 64-byte blocks
111148
var _ofs = parseInt(log.stack.peek(0).toString());
112149
var _len = parseInt(log.stack.peek(1).toString());
113-
114-
if (_len < 512) {
150+
// currently, solidity uses only 2-word (6-byte) for a key. this might change..
151+
// still, no need to return too much
152+
if (_len > 20 && _len < 512) {
153+
// if (len == 64) {
115154
this.keccak.push(toHex(log.memory.slice(_ofs, _ofs + _len)));
116155
}
117156
} else if (opcode.startsWith("LOG")) {
118157
var count = parseInt(opcode.substring(3));
119158
var _ofs2 = parseInt(log.stack.peek(0).toString());
120159
var _len2 = parseInt(log.stack.peek(1).toString());
121-
122160
var topics = [];
123161
for (var i = 0; i < count; i++) {
162+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
124163
topics.push("0x" + log.stack.peek(2 + i).toString(16));
125164
}
126-
165+
var _data = toHex(log.memory.slice(_ofs2, _ofs2 + _len2));
127166
this.logs.push({
128167
topics: topics,
129-
data: toHex(log.memory.slice(_ofs2, _ofs2 + _len2)),
168+
data: _data,
130169
});
131170
}
132171
},

pkg/tracer/types.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@ type LogInfo struct {
3535

3636
// BundlerCollectorReturn is the return value from performing an EVM trace with BundlerCollectorTracer.js.
3737
type BundlerCollectorReturn struct {
38-
NumberLevels map[string]NumberLevelInfo `json:"numberLevels"`
39-
Keccak []string `json:"keccak"`
40-
Calls []CallInfo `json:"calls"`
41-
Logs []LogInfo `json:"logs"`
42-
Debug []any `json:"debug"`
38+
NumberLevels []NumberLevelInfo `json:"numberLevels"`
39+
Keccak []string `json:"keccak"`
40+
Calls []CallInfo `json:"calls"`
41+
Logs []LogInfo `json:"logs"`
42+
Debug []any `json:"debug"`
4343
}

0 commit comments

Comments
 (0)