32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
|
# File 'lib/ethereum/fast_vm.rb', line 32
def execute(ext, msg, code)
s = State.new gas: msg.gas
if VM.code_cache.has_key?(code)
processed_code = VM.code_cache[code]
else
processed_code = preprocess_code code
VM.code_cache[code] = processed_code
end
steps = 0
_prevop = nil
timestamp = Time.now
loop do
return vm_exception('INVALID START POINT') if processed_code.has_key?(s.pc)
cc = processed_code[s.pc]
gas, min_stack, max_stack, s.pc = cc[0,4]
ops = cc[4..-1]
return vm_exception('OUT OF GAS') if gas > s.gas
return vm_exception('INCOMPATIBLE STACK LENGTH', min_stack: min_stack, max_stack: max_stack, have: s.stack.size) unless s.stack.size >= min_stack && s.stack.size <= max_stack
s.gas -= gas
ops.each do |op|
if Logger.trace?(log_vm_exit.name)
trace_data = {
stack: s.stack.map(&:to_s),
inst: op,
pc: s.pc-1,
op: op,
steps: steps
}
if [OP_MLOAD, OP_MSTORE, OP_MSTORE8, OP_SHA3, OP_CALL, OP_CALLCODE, OP_CREATE, OP_CALLDATACOPY, OP_CODECOPY, OP_EXTCODECOPY].include?(_prevop)
if s.memory.size < 1024
trace_data[:memory] = Utils.encode_hex(Utils.int_array_to_bytes(s.memory))
else
trace_data[:sha3memory] = Utils.encode_hex(Utils.keccak256(Utils.int_array_to_bytes(s.memory)))
end
end
if [OP_SSTORE, OP_SLOAD].include?(_prevop) || steps == 0
trace_data[:storage] = ext.log_storage(msg.to)
end
if steps == 0
trace_data[:depth] = msg.depth
trace_data[:address] = msg.to
end
log_vm_op.trace('vm', **trace_data)
steps += 1
_prevop = op
end
return vm_exception('INVALID OP', op: op) if op == OP_INVALID
stk = s.stack
mem = s.memory
if op < 0x10
case op
when OP_STOP
return peaceful_exit('STOP', s.gas, [])
when OP_ADD
r = (stk.pop + stk.pop) & UINT_MAX
stk.push r
when OP_SUB
r = (stk.pop - stk.pop) & UINT_MAX
stk.push r
when OP_MUL
r = (stk.pop * stk.pop) & UINT_MAX
stk.push r
when OP_DIV
s0, s1 = stk.pop, stk.pop
stk.push(s1 == 0 ? 0 : s0 / s1)
when OP_MOD
s0, s1 = stk.pop, stk.pop
stk.push(s1 == 0 ? 0 : s0 % s1)
when OP_SDIV
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
r = s1 == 0 ? 0 : ((s0.abs / s1.abs * (s0*s1 < 0 ? -1 : 1)) & UINT_MAX)
stk.push r
when OP_SMOD
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
r = s1 == 0 ? 0 : ((s0.abs % s1.abs * (s0 < 0 ? -1 : 1)) & UINT_MAX)
stk.push r
when OP_ADDMOD
s0, s1, s2 = stk.pop, stk.pop, stk.pop
r = s2 == 0 ? 0 : (s0+s1) % s2
stk.push r
when OP_MULMOD
s0, s1, s2 = stk.pop, stk.pop, stk.pop
r = s2 == 0 ? 0 : Utils.mod_mul(s0, s1, s2)
stk.push r
when OP_EXP
base, exponent = stk.pop, stk.pop
nbytes = Utils.encode_int(exponent).size
expfee = nbytes * Opcodes::GEXPONENTBYTE
if s.gas < expfee
s.gas = 0
return vm_exception('OOG EXPONENT')
end
s.gas -= expfee
stk.push Utils.mod_exp(base, exponent, TT256)
when OP_SIGNEXTEND
s0, s1 = stk.pop, stk.pop
if s0 < 32
testbit = s0*8 + 7
mask = 1 << testbit
if s1 & mask == 0
stk.push(s1 & (mask - 1))
else
stk.push(s1 | (TT256 - mask))
end
else
stk.push s1
end
end
elsif op < 0x20
case op
when OP_LT
s0, s1 = stk.pop, stk.pop
stk.push(s0 < s1 ? 1 : 0)
when OP_GT
s0, s1 = stk.pop, stk.pop
stk.push(s0 > s1 ? 1 : 0)
when OP_SLT
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
stk.push(s0 < s1 ? 1 : 0)
when OP_SGT
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
stk.push(s0 > s1 ? 1 : 0)
when OP_EQ
s0, s1 = stk.pop, stk.pop
stk.push(s0 == s1 ? 1 : 0)
when OP_ISZERO
s0 = stk.pop
stk.push(s0 == 0 ? 1 : 0)
when OP_AND
s0, s1 = stk.pop, stk.pop
stk.push(s0 & s1)
when OP_OR
s0, s1 = stk.pop, stk.pop
stk.push(s0 | s1)
when OP_XOR
s0, s1 = stk.pop, stk.pop
stk.push(s0 ^ s1)
when OP_NOT
s0 = stk.pop
stk.push(UINT_MAX - s0)
when OP_BYTE
s0, s1 = stk.pop, stk.pop
if s0 < 32
stk.push((s1 / 256**(31-s0)) % 256)
else
stk.push(0)
end
end
elsif op < 0x40
case op
when OP_SHA3
s0, s1 = stk.pop, stk.pop
s.gas -= Opcodes::GSHA3WORD * (Utils.ceil32(s1) / 32)
return vm_exception('OOG PAYING FOR SHA3') if s.gas < 0
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
data = Utils.int_array_to_bytes mem.safe_slice(s0,s1)
stk.push Utils.big_endian_to_int(Utils.keccak256(data))
when OP_ADDRESS
stk.push Utils.coerce_to_int(msg.to)
when OP_BALANCE
s0 = stk.pop
addr = Utils.coerce_addr_to_hex(s0 % 2**160)
stk.push ext.get_balance(addr)
when OP_ORIGIN
stk.push Utils.coerce_to_int(ext.tx_origin)
when OP_CALLER
stk.push Utils.coerce_to_int(msg.sender)
when OP_CALLVALUE
stk.push msg.value
when OP_CALLDATALOAD
stk.push msg.data.(stk.pop)
when OP_CALLDATASIZE
stk.push msg.data.size
when OP_CALLDATACOPY
mstart, dstart, size = stk.pop, stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
return vm_exception('OOG COPY DATA') unless data_copy(s, size)
msg.data.(mem, mstart, dstart, size)
when OP_CODESIZE
stk.push code.size
when OP_CODECOPY
mstart, cstart, size = stk.pop, stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
return vm_exception('OOG COPY CODE') unless data_copy(s, size)
size.times do |i|
if cstart + i < code.size
mem[mstart+i] = code[cstart+i].ord
else
mem[mstart+i] = 0
end
end
when OP_GASPRICE
stk.push ext.tx_gasprice
when OP_EXTCODESIZE
addr = stk.pop
addr = Utils.coerce_addr_to_hex(addr % 2**160)
stk.push (ext.get_code(addr) || Constant::BYTE_EMPTY).size
when OP_EXTCODECOPY
addr, mstart, cstart, size = stk.pop, stk.pop, stk.pop, stk.pop
addr = Utils.coerce_addr_to_hex(addr % 2**160)
extcode = ext.get_code(addr) || Constant::BYTE_EMPTY
raise ValueError, "extcode must be string" unless extcode.is_a?(String)
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
return vm_exception('OOG COPY CODE') unless data_copy(s, size)
size.times do |i|
if cstart + i < extcode.size
mem[mstart+i] = extcode[cstart+i].ord
else
mem[mstart+i] = 0
end
end
end
elsif op < 0x50
case op
when OP_BLOCKHASH
s0 = stk.pop
stk.push Utils.big_endian_to_int(ext.block_hash(s0))
when OP_COINBASE
stk.push Utils.big_endian_to_int(ext.block_coinbase)
when OP_TIMESTAMP
stk.push ext.block_timestamp
when OP_NUMBER
stk.push ext.block_number
when OP_DIFFICULTY
stk.push ext.block_difficulty
when OP_GASLIMIT
stk.push ext.block_gas_limit
end
elsif op < 0x60
case op
when OP_POP
stk.pop
when OP_MLOAD
s0 = stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)
data = 0
mem[s0, 32].each do |c|
data = (data << 8) + c
end
stk.push data
when OP_MSTORE
s0, s1 = stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)
32.times.to_a.reverse.each do |i|
mem[s0+i] = s1 % 256
s1 /= 256
end
when OP_MSTORE8
s0, s1 = stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 1)
mem[s0] = s1 % 256
when OP_SLOAD
s0 = stk.pop
stk.push ext.get_storage_data(msg.to, s0)
when OP_SSTORE
s0, s1 = stk.pop, stk.pop
if ext.get_storage_data(msg.to, s0) != 0
gascost = s1 == 0 ? Opcodes::GSTORAGEKILL : Opcodes::GSTORAGEMOD
refund = s1 == 0 ? Opcodes::GSTORAGEREFUND : 0
else
gascost = s1 == 0 ? Opcodes::GSTORAGEMOD : Opcodes::GSTORAGEADD
refund = 0
end
return vm_exception('OUT OF GAS') if s.gas < gascost
s.gas -= gascost
ext.add_refund refund
ext.set_storage_data msg.to, s0, s1
when OP_JUMP
s0 = stk.pop
s.pc = s0
op_new = processed_code.has_key?(s.pc) ? processed_code[s.pc][4] : OP_STOP
return vm_exception('BAD JUMPDEST') if op_new != OP_JUMPDEST
when OP_JUMPI
s0, s1 = stk.pop, stk.pop
if s1.true?
s.pc = s0
op_new = processed_code.has_key?(s.pc) ? processed_code[s.pc][4] : OP_STOP
return vm_exception('BAD JUMPDEST') if op_new != OP_JUMPDEST
end
when OP_PC
stk.push(s.pc - 1)
when OP_MSIZE
stk.push mem.size
when OP_GAS
stk.push s.gas
end
elsif (op & 0xff) >= OP_PUSH1 && (op & 0xff) <= OP_PUSH32
stk.push(op >> 8)
elsif op >= OP_DUP1 && op <= OP_DUP16
depth = op - OP_DUP1 + 1
stk.push stk[-depth]
elsif op >= OP_SWAP1 && op <= OP_SWAP16
depth = op - OP_SWAP1 + 1
temp = stk[-depth - 1]
stk[-depth - 1] = stk[-1]
stk[-1] = temp
elsif op >= OP_LOG0 && op <= OP_LOG4
depth = op - OP_LOG0
mstart, msz = stk.pop, stk.pop
topics = depth.times.map {|i| stk.pop }
s.gas -= msz * Opcodes::GLOGBYTE
return vm_exception("OOG EXTENDING MEMORY") unless mem_extend(mem, s, mstart, msz)
data = mem.safe_slice(mstart, msz)
ext.log(msg.to, topics, Utils.int_array_to_bytes(data))
log_log.trace('LOG', to: msg.to, topics: topics, data: data)
elsif op == OP_CREATE
value, mstart, msz = stk.pop, stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, msz)
if ext.get_balance(msg.to) >= value && msg.depth < 1024
cd = CallData.new mem, mstart, msz
create_msg = Message.new(msg.to, Constant::BYTE_EMPTY, value, s.gas, cd, depth: msg.depth+1)
o, gas, addr = ext.create create_msg
if o.true?
stk.push Utils.coerce_to_int(addr)
s.gas = gas
else
stk.push 0
s.gas = 0
end
else
stk.push(0)
end
elsif op == OP_CALL
gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
to = Utils.zpad_int(to)[12..-1]
= (ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT +
(value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
total_gas = gas +
return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
if ext.get_balance(msg.to) >= value && msg.depth < 1024
s.gas -= total_gas
cd = CallData.new mem, memin_start, memin_sz
call_msg = Message.new(msg.to, to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
result, gas, data = ext.apply_msg call_msg
if result == 0
stk.push 0
else
stk.push 1
s.gas += gas
[data.size, memout_sz].min.times do |i|
mem[memout_start+i] = data[i]
end
end
else
s.gas -= (total_gas - submsg_gas)
stk.push(0)
end
elsif op == OP_CALLCODE
gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
= (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
total_gas = gas +
return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
if ext.get_balance(msg.to) >= value && msg.depth < 1024
s.gas -= total_gas
to = Utils.zpad_int(to)[12..-1]
cd = CallData.new mem, memin_start, memin_sz
call_msg = Message.new(msg.to, msg.to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
result, gas, data = ext.apply_msg call_msg
if result == 0
stk.push 0
else
stk.push 1
s.gas += gas
[data.size, memout_sz].min.times do |i|
mem[memout_start+i] = data[i]
end
end
else
s.gas -= (total_gas - submsg_gas)
stk.push(0)
end
elsif op == OP_RETURN
s0, s1 = stk.pop, stk.pop
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
return peaceful_exit('RETURN', s.gas, mem.safe_slice(s0, s1))
elsif op == OP_SUICIDE
s0 = stk.pop
to = Utils.zpad_int(s0)[12..-1]
xfer = ext.get_balance(msg.to)
ext.set_balance(to, ext.get_balance(to)+xfer)
ext.set_balance(msg.to, 0)
ext.add_suicide(msg.to)
return 1, s.gas, []
end
end
end
end
|