34
34
35
35
MAX_DEPTH = 1024
36
36
37
+ # Wrapper to store call data. This is needed because it is possible to
38
+ # call a contract N times with N bytes of data with a gas cost of O(N);
39
+ # if implemented naively this would require O(N**2) bytes of data
40
+ # copying. Instead we just copy the reference to the parent memory
41
+ # slice plus the start and end of the slice
37
42
class CallData (object ):
38
43
39
44
def __init__ (self , parent_memory , offset = 0 , size = None ):
@@ -42,18 +47,21 @@ def __init__(self, parent_memory, offset=0, size=None):
42
47
self .size = len (self .data ) if size is None else size
43
48
self .rlimit = self .offset + self .size
44
49
50
+ # Convert calldata to bytes
45
51
def extract_all (self ):
46
52
d = self .data [self .offset : self .offset + self .size ]
47
53
d .extend (bytearray (self .size - len (d )))
48
54
return bytes (bytearray (d ))
49
55
56
+ # Extract 32 bytes as integer
50
57
def extract32 (self , i ):
51
58
if i >= self .size :
52
59
return 0
53
60
o = self .data [self .offset + i : min (self .offset + i + 32 , self .rlimit )]
54
61
o .extend (bytearray (32 - len (o )))
55
62
return utils .bytearray_to_int (o )
56
63
64
+ # Extract a slice and copy it to memory
57
65
def extract_copy (self , mem , memstart , datastart , size ):
58
66
for i in range (size ):
59
67
if datastart + i < self .size :
@@ -62,6 +70,8 @@ def extract_copy(self, mem, memstart, datastart, size):
62
70
mem [memstart + i ] = 0
63
71
64
72
73
+ # Stores a message object, including context data like sender,
74
+ # destination, gas, whether or not it is a STATICCALL, etc
65
75
class Message (object ):
66
76
67
77
def __init__ (self , sender , to , value = 0 , gas = 1000000 , data = '' , depth = 0 ,
@@ -82,6 +92,7 @@ def __repr__(self):
82
92
return '<Message(to:%s...)>' % self .to [:8 ]
83
93
84
94
95
+ # Virtual machine state of the current EVM instance
85
96
class Compustate ():
86
97
87
98
def __init__ (self , ** kwargs ):
@@ -113,6 +124,7 @@ def preprocess_code(code):
113
124
return o , pushcache
114
125
115
126
127
+ # Extends memory, and pays gas for it
116
128
def mem_extend (mem , compustate , op , start , sz ):
117
129
if sz and start + sz > len (mem ):
118
130
oldsize = len (mem ) // 32
@@ -131,16 +143,12 @@ def mem_extend(mem, compustate, op, start, sz):
131
143
return True
132
144
133
145
146
+ # Pays gas for copying data
134
147
def data_copy (compustate , size ):
135
- if size :
136
- copyfee = opcodes .GCOPY * utils .ceil32 (size ) // 32
137
- if compustate .gas < copyfee :
138
- compustate .gas = 0
139
- return False
140
- compustate .gas -= copyfee
141
- return True
148
+ return eat_gas (compustate , opcodes .GCOPY * utils .ceil32 (size ) // 32 )
142
149
143
150
151
+ # Consumes a given amount of gas
144
152
def eat_gas (compustate , amount ):
145
153
if compustate .gas < amount :
146
154
compustate .gas = 0
@@ -150,48 +158,50 @@ def eat_gas(compustate, amount):
150
158
return True
151
159
152
160
161
+ # Used to compute maximum amount of gas for child calls
153
162
def all_but_1n (x , n ):
154
163
return x - x // n
155
164
156
165
166
+ # Throws a VM exception
157
167
def vm_exception (error , ** kargs ):
158
168
log_vm_exit .trace ('EXCEPTION' , cause = error , ** kargs )
159
169
return 0 , 0 , []
160
170
161
171
172
+ # Peacefully exits the VM
162
173
def peaceful_exit (cause , gas , data , ** kargs ):
163
174
log_vm_exit .trace ('EXIT' , cause = cause , ** kargs )
164
175
return 1 , gas , data
165
176
166
177
178
+ # Exits with the REVERT opcode
167
179
def revert (gas , data , ** kargs ):
168
180
log_vm_exit .trace ('REVERT' , ** kargs )
169
181
return 0 , gas , data
170
182
171
- code_cache = {}
172
-
173
183
184
+ # Main function
174
185
def vm_execute (ext , msg , code ):
175
186
# precompute trace flag
176
187
# if we trace vm, we're in slow mode anyway
177
188
trace_vm = log_vm_op .is_active ('trace' )
178
189
190
+ # Initialize stack, memory, program counter, etc
179
191
compustate = Compustate (gas = msg .gas )
180
192
stk = compustate .stack
181
193
mem = compustate .memory
182
194
195
+ # Compute
183
196
jumpdest_mask , pushcache = preprocess_code (code )
184
197
codelen = len (code )
185
- code += b'\x00 ' * 32
186
198
199
+ # For tracing purposes
187
200
op = None
188
201
steps = 0
189
202
_prevop = None # for trace only
190
203
191
204
while compustate .pc < codelen :
192
- # print('op: ', op, time.time() - s)
193
- # s = time.time()
194
- # stack size limit error
195
205
196
206
opcode = code [compustate .pc ]
197
207
@@ -211,6 +221,7 @@ def vm_execute(ext, msg, code):
211
221
op = op , needed = to_string (in_args ),
212
222
available = to_string (len (compustate .stack )))
213
223
224
+ # overfull stack error
214
225
if len (compustate .stack ) - in_args + out_args > 1024 :
215
226
return vm_exception ('STACK SIZE LIMIT EXCEEDED' ,
216
227
op = op ,
@@ -220,6 +231,7 @@ def vm_execute(ext, msg, code):
220
231
compustate .gas -= fee
221
232
compustate .pc += 1
222
233
234
+ # Tracing
223
235
if trace_vm :
224
236
"""
225
237
This diverges from normal logging, as we use the logging namespace
@@ -261,6 +273,7 @@ def vm_execute(ext, msg, code):
261
273
if 0x60 <= opcode <= 0x7f :
262
274
stk .append (pushcache [compustate .pc - 1 ])
263
275
compustate .pc += opcode - 0x5f # Move 1 byte forward for 0x60, up to 32 bytes for 0x7f
276
+ # Arithmetic
264
277
elif opcode < 0x10 :
265
278
if op == 'STOP' :
266
279
return peaceful_exit ('STOP' , compustate .gas , [])
@@ -313,6 +326,7 @@ def vm_execute(ext, msg, code):
313
326
stk .append (s1 & ((1 << testbit ) - 1 ))
314
327
else :
315
328
stk .append (s1 )
329
+ # Comparisons
316
330
elif opcode < 0x20 :
317
331
if op == 'LT' :
318
332
stk .append (1 if stk .pop () < stk .pop () else 0 )
@@ -342,6 +356,7 @@ def vm_execute(ext, msg, code):
342
356
stk .append (0 )
343
357
else :
344
358
stk .append ((s1 // 256 ** (31 - s0 )) % 256 )
359
+ # SHA3 and environment info
345
360
elif opcode < 0x40 :
346
361
if op == 'SHA3' :
347
362
s0 , s1 = stk .pop (), stk .pop ()
@@ -426,6 +441,7 @@ def vm_execute(ext, msg, code):
426
441
mem [start + i ] = utils .safe_ord (extcode [s2 + i ])
427
442
else :
428
443
mem [start + i ] = 0
444
+ # Block info
429
445
elif opcode < 0x50 :
430
446
if op == 'BLOCKHASH' :
431
447
if ext .post_metropolis_hardfork () and False :
@@ -443,6 +459,7 @@ def vm_execute(ext, msg, code):
443
459
stk .append (ext .block_difficulty )
444
460
elif op == 'GASLIMIT' :
445
461
stk .append (ext .block_gas_limit )
462
+ # VM state manipulations
446
463
elif opcode < 0x60 :
447
464
if op == 'POP' :
448
465
stk .pop ()
@@ -497,13 +514,15 @@ def vm_execute(ext, msg, code):
497
514
stk .append (len (mem ))
498
515
elif op == 'GAS' :
499
516
stk .append (compustate .gas ) # AFTER subtracting cost 1
517
+ # DUPn (eg. DUP1: a b c -> a b c c, DUP3: a b c -> a b c a)
500
518
elif op [:3 ] == 'DUP' :
501
519
stk .append (stk [0x7f - opcode ]) # 0x7f - opcode is a negative number, -1 for 0x80 ... -16 for 0x8f
520
+ # SWAPn (eg. SWAP1: a b c d -> a b d c, SWAP3: a b c d -> d b c a)
502
521
elif op [:4 ] == 'SWAP' :
503
522
temp = stk [0x8e - opcode ] # 0x8e - opcode is a negative number, -2 for 0x90 ... -17 for 0x9f
504
523
stk [0x8e - opcode ] = stk [- 1 ]
505
524
stk [- 1 ] = temp
506
-
525
+ # Logs (aka "events")
507
526
elif op [:3 ] == 'LOG' :
508
527
"""
509
528
0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas
@@ -529,7 +548,7 @@ def vm_execute(ext, msg, code):
529
548
ext .log (msg .to , topics , data )
530
549
log_log .trace ('LOG' , to = msg .to , topics = topics , data = list (map (utils .safe_ord , data )))
531
550
# print('LOG', msg.to, topics, list(map(ord, data)))
532
-
551
+ # Create a new contract
533
552
elif op == 'CREATE' :
534
553
value , mstart , msz = stk .pop (), stk .pop (), stk .pop ()
535
554
if not mem_extend (mem , compustate , op , mstart , msz ):
@@ -550,6 +569,7 @@ def vm_execute(ext, msg, code):
550
569
compustate .gas = compustate .gas - ingas + gas
551
570
else :
552
571
stk .append (0 )
572
+ # Calls
553
573
elif op in ('CALL' , 'CALLCODE' , 'DELEGATECALL' , 'STATICCALL' ):
554
574
# Pull arguments from the stack
555
575
if op in ('CALL' , 'CALLCODE' ):
@@ -567,10 +587,17 @@ def vm_execute(ext, msg, code):
567
587
not mem_extend (mem , compustate , op , memoutstart , memoutsz ):
568
588
return vm_exception ('OOG EXTENDING MEMORY' )
569
589
to = utils .int_to_addr (to )
570
- # Extra gas costs based on hard fork-dependent factors
571
- extra_gas = (not ext .account_exists (to )) * (op == 'CALL' ) * (value > 0 or not ext .post_spurious_dragon_hardfork ()) * opcodes .GCALLNEWACCOUNT + \
572
- (value > 0 ) * opcodes .GCALLVALUETRANSFER + \
573
- ext .post_anti_dos_hardfork () * opcodes .CALL_SUPPLEMENTAL_GAS
590
+ # Extra gas costs based on various factors
591
+ extra_gas = 0
592
+ # Creating a new account
593
+ if op == 'CALL' and not ext .account_exists (to ) and (value > 0 or not ext .post_spurious_dragon_hardfork ()):
594
+ extra_gas += opcodes .GCALLNEWACCOUNT
595
+ # Value transfer
596
+ if value > 0 :
597
+ extra_gas += opcodes .GCALLVALUETRANSFER
598
+ # Cost increased from 40 to 700 in Tangerine Whistle
599
+ if ext .post_anti_dos_hardfork ():
600
+ extra_gas += opcodes .CALL_SUPPLEMENTAL_GAS
574
601
# Compute child gas limit
575
602
if ext .post_anti_dos_hardfork ():
576
603
if compustate .gas < extra_gas :
@@ -617,18 +644,21 @@ def vm_execute(ext, msg, code):
617
644
mem [memoutstart + i ] = data [i ]
618
645
compustate .gas += gas
619
646
compustate .last_returned = bytearray (data )
647
+ # Return opcode
620
648
elif op == 'RETURN' :
621
649
s0 , s1 = stk .pop (), stk .pop ()
622
650
if not mem_extend (mem , compustate , op , s0 , s1 ):
623
651
return vm_exception ('OOG EXTENDING MEMORY' )
624
652
return peaceful_exit ('RETURN' , compustate .gas , mem [s0 : s0 + s1 ])
653
+ # Revert opcode (Metropolis)
625
654
elif op == 'REVERT' :
626
655
if not ext .post_metropolis_hardfork ():
627
656
return vm_exception ('Opcode not yet enabled' )
628
657
s0 , s1 = stk .pop (), stk .pop ()
629
658
if not mem_extend (mem , compustate , op , s0 , s1 ):
630
659
return vm_exception ('OOG EXTENDING MEMORY' )
631
660
return revert (compustate .gas , mem [s0 : s0 + s1 ])
661
+ # SUICIDE opcode (also called SELFDESTRUCT)
632
662
elif op == 'SUICIDE' :
633
663
if msg .static :
634
664
return vm_exception ('Cannot SUICIDE inside a static context' )
@@ -644,16 +674,13 @@ def vm_execute(ext, msg, code):
644
674
ext .set_balance (msg .to , 0 )
645
675
ext .add_suicide (msg .to )
646
676
log_msg .debug ('SUICIDING' , addr = utils .checksum_encode (msg .to ), to = utils .checksum_encode (to ), xferring = xfer )
647
- return 1 , compustate .gas , []
677
+ return peaceful_exit ( 'SUICIDED' , compustate .gas , [])
648
678
649
- # assert utils.is_numeric(compustate.gas)
650
- # this is slow!
651
- # for a in stk:
652
- # assert is_numeric(a), (op, stk)
653
- # assert a >= 0 and a < 2**256, (a, op, stk)
654
679
return peaceful_exit ('CODE OUT OF RANGE' , compustate .gas , [])
655
680
656
681
682
+ # A stub that's mainly here to show what you would need to implement to
683
+ # hook into the EVM
657
684
class VmExtBase ():
658
685
659
686
def __init__ (self ):
0 commit comments