1
1
// This is the same BundlerCollectorTracer from github.com/eth-infinitism/bundler transpiled down to ES5.
2
2
3
3
var tracer = {
4
- numberLevels : { } ,
4
+ numberLevels : [ ] ,
5
5
currentLevel : null ,
6
6
keccak : [ ] ,
7
7
calls : [ ] ,
8
8
logs : [ ] ,
9
9
debug : [ ] ,
10
10
lastOp : "" ,
11
11
numberCounter : 0 ,
12
- count : 0 ,
13
12
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
+ ) ;
16
24
} ,
17
-
18
- result : function ( ctx , db ) {
25
+ result : function result ( ctx , db ) {
19
26
return {
20
27
numberLevels : this . numberLevels ,
21
28
keccak : this . keccak ,
22
29
logs : this . logs ,
23
30
calls : this . calls ,
24
- debug : this . debug ,
31
+ debug : this . debug , // for internal debugging.
25
32
} ;
26
33
} ,
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=" +
31
39
frame . getType ( ) +
32
- " " +
40
+ " to= " +
33
41
toHex ( frame . getTo ( ) ) +
34
- " " +
35
- toHex ( frame . getInput ( ) ) . slice ( 0 , 100 ) ,
36
- ] ) ;
37
-
42
+ " in=" +
43
+ toHex ( frame . getInput ( ) ) . slice ( 0 , 500 )
44
+ ) ;
38
45
this . calls . push ( {
39
46
type : frame . getType ( ) ,
40
47
from : toHex ( frame . getFrom ( ) ) ,
41
48
to : toHex ( frame . getTo ( ) ) ,
49
+ method : toHex ( frame . getInput ( ) ) . slice ( 0 , 10 ) ,
50
+ gas : frame . getGas ( ) ,
42
51
value : frame . getValue ( ) ,
43
52
} ) ;
44
53
} ,
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
+ } ) ;
50
60
} ,
51
61
62
+ // increment the "key" in the list. if the key is not defined yet, then set it to "1"
52
63
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 ;
54
66
} ,
55
-
56
67
step : function step ( log , db ) {
57
68
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
+ }
58
73
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
+ }
66
88
}
67
89
68
90
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 = "" ;
69
101
return ;
70
102
}
71
103
72
104
if ( this . lastOp === "GAS" && ! opcode . includes ( "CALL" ) ) {
105
+ // count "GAS" opcode only if not followed by "CALL"
73
106
this . countSlot ( this . currentLevel . opcodes , "GAS" ) ;
74
107
}
75
-
76
108
if ( opcode !== "GAS" ) {
109
+ // ignore "unimportant" opcodes:
77
110
if (
78
111
opcode . match (
79
112
/ ^ ( D U P \d + | P U S H \d + | S W A P \d + | P O P | A D D | S U B | M U L | D I V | E Q | L T E ? | S ? G T E ? | S L T | S H [ L R ] | A N D | O R | N O T | I S Z E R O ) $ /
@@ -82,51 +115,57 @@ var tracer = {
82
115
this . countSlot ( this . currentLevel . opcodes , opcode ) ;
83
116
}
84
117
}
118
+ if (
119
+ opcode . match ( / ^ ( E X T .* | C A L L | C A L L C O D E | D E L E G A T E C A L L | S T A T I C C A L L | C R E A T E 2 ) $ / ) !=
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
+ }
85
130
86
131
this . lastOp = opcode ;
87
132
88
133
if ( opcode === "SLOAD" || opcode === "SSTORE" ) {
89
134
var slot = log . stack . peek ( 0 ) . toString ( 16 ) ;
90
- var addr = toHex ( log . contract . getAddress ( ) ) ;
135
+ var _addr = toHex ( log . contract . getAddress ( ) ) ;
91
136
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 = {
95
139
reads : { } ,
96
140
writes : { } ,
97
141
} ;
98
142
}
99
-
100
143
this . countSlot ( opcode === "SLOAD" ? access . reads : access . writes , slot ) ;
101
144
}
102
145
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
111
148
var _ofs = parseInt ( log . stack . peek ( 0 ) . toString ( ) ) ;
112
149
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) {
115
154
this . keccak . push ( toHex ( log . memory . slice ( _ofs , _ofs + _len ) ) ) ;
116
155
}
117
156
} else if ( opcode . startsWith ( "LOG" ) ) {
118
157
var count = parseInt ( opcode . substring ( 3 ) ) ;
119
158
var _ofs2 = parseInt ( log . stack . peek ( 0 ) . toString ( ) ) ;
120
159
var _len2 = parseInt ( log . stack . peek ( 1 ) . toString ( ) ) ;
121
-
122
160
var topics = [ ] ;
123
161
for ( var i = 0 ; i < count ; i ++ ) {
162
+ // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
124
163
topics . push ( "0x" + log . stack . peek ( 2 + i ) . toString ( 16 ) ) ;
125
164
}
126
-
165
+ var _data = toHex ( log . memory . slice ( _ofs2 , _ofs2 + _len2 ) ) ;
127
166
this . logs . push ( {
128
167
topics : topics ,
129
- data : toHex ( log . memory . slice ( _ofs2 , _ofs2 + _len2 ) ) ,
168
+ data : _data ,
130
169
} ) ;
131
170
}
132
171
} ,
0 commit comments