ruby-ethereum 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +40 -0
  4. data/lib/ethereum.rb +53 -0
  5. data/lib/ethereum/abi.rb +333 -0
  6. data/lib/ethereum/abi/contract_translator.rb +174 -0
  7. data/lib/ethereum/abi/type.rb +117 -0
  8. data/lib/ethereum/account.rb +72 -0
  9. data/lib/ethereum/address.rb +60 -0
  10. data/lib/ethereum/base_convert.rb +53 -0
  11. data/lib/ethereum/block.rb +1311 -0
  12. data/lib/ethereum/block_header.rb +211 -0
  13. data/lib/ethereum/bloom.rb +83 -0
  14. data/lib/ethereum/cached_block.rb +36 -0
  15. data/lib/ethereum/chain.rb +400 -0
  16. data/lib/ethereum/constant.rb +26 -0
  17. data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
  18. data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
  19. data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
  20. data/lib/ethereum/core_ext/object/truth.rb +47 -0
  21. data/lib/ethereum/db.rb +6 -0
  22. data/lib/ethereum/db/base_db.rb +9 -0
  23. data/lib/ethereum/db/ephem_db.rb +63 -0
  24. data/lib/ethereum/db/overlay_db.rb +72 -0
  25. data/lib/ethereum/db/refcount_db.rb +188 -0
  26. data/lib/ethereum/env.rb +64 -0
  27. data/lib/ethereum/ethash.rb +78 -0
  28. data/lib/ethereum/ethash_ruby.rb +38 -0
  29. data/lib/ethereum/ethash_ruby/cache.rb +47 -0
  30. data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
  31. data/lib/ethereum/ethash_ruby/utils.rb +53 -0
  32. data/lib/ethereum/exceptions.rb +28 -0
  33. data/lib/ethereum/external_call.rb +173 -0
  34. data/lib/ethereum/fast_rlp.rb +81 -0
  35. data/lib/ethereum/fast_vm.rb +625 -0
  36. data/lib/ethereum/fast_vm/call_data.rb +44 -0
  37. data/lib/ethereum/fast_vm/message.rb +29 -0
  38. data/lib/ethereum/fast_vm/state.rb +25 -0
  39. data/lib/ethereum/ffi/openssl.rb +390 -0
  40. data/lib/ethereum/index.rb +97 -0
  41. data/lib/ethereum/log.rb +43 -0
  42. data/lib/ethereum/miner.rb +84 -0
  43. data/lib/ethereum/opcodes.rb +131 -0
  44. data/lib/ethereum/private_key.rb +92 -0
  45. data/lib/ethereum/pruning_trie.rb +28 -0
  46. data/lib/ethereum/public_key.rb +88 -0
  47. data/lib/ethereum/receipt.rb +53 -0
  48. data/lib/ethereum/secp256k1.rb +58 -0
  49. data/lib/ethereum/secure_trie.rb +49 -0
  50. data/lib/ethereum/sedes.rb +42 -0
  51. data/lib/ethereum/special_contract.rb +95 -0
  52. data/lib/ethereum/spv.rb +79 -0
  53. data/lib/ethereum/spv/proof.rb +31 -0
  54. data/lib/ethereum/spv/proof_constructor.rb +19 -0
  55. data/lib/ethereum/spv/proof_verifier.rb +24 -0
  56. data/lib/ethereum/tester.rb +14 -0
  57. data/lib/ethereum/tester/abi_contract.rb +65 -0
  58. data/lib/ethereum/tester/fixture.rb +31 -0
  59. data/lib/ethereum/tester/language.rb +30 -0
  60. data/lib/ethereum/tester/log_recorder.rb +13 -0
  61. data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
  62. data/lib/ethereum/tester/state.rb +194 -0
  63. data/lib/ethereum/transaction.rb +196 -0
  64. data/lib/ethereum/transient_trie.rb +28 -0
  65. data/lib/ethereum/trie.rb +549 -0
  66. data/lib/ethereum/trie/nibble_key.rb +184 -0
  67. data/lib/ethereum/utils.rb +191 -0
  68. data/lib/ethereum/version.rb +5 -0
  69. data/lib/ethereum/vm.rb +606 -0
  70. data/lib/ethereum/vm/call_data.rb +40 -0
  71. data/lib/ethereum/vm/message.rb +30 -0
  72. data/lib/ethereum/vm/state.rb +25 -0
  73. metadata +284 -0
@@ -0,0 +1,81 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ module FastRLP
5
+ include RLP::Encode
6
+
7
+ ##
8
+ # RLP encode (a nested list of ) bytes.
9
+ #
10
+ def encode_nested_bytes(item)
11
+ if item.instance_of?(String)
12
+ return item if item.size == 1 && item.ord < PRIMITIVE_PREFIX_OFFSET
13
+ prefix = length_prefix item.size, PRIMITIVE_PREFIX_OFFSET
14
+ else # list
15
+ item = item.map {|x| encode_nested_bytes(x) }.join
16
+ prefix = length_prefix item.size, LIST_PREFIX_OFFSET
17
+ end
18
+
19
+ "#{prefix}#{item}"
20
+ end
21
+
22
+ ##
23
+ # Alias to encode_nested_bytes, override default encode.
24
+ #
25
+ def encode(item)
26
+ encode_nested_bytes item
27
+ end
28
+
29
+ def decode(rlp)
30
+ o = []
31
+ pos = 0
32
+
33
+ type, len, pos = consume_length_prefix rlp, pos
34
+ return rlp[pos, len] if type != :list
35
+
36
+ while pos < rlp.size
37
+ _, _len, _pos = consume_length_prefix rlp, pos
38
+ to = _len + _pos
39
+ o.push decode(rlp[pos...to])
40
+ pos = to
41
+ end
42
+
43
+ o
44
+ end
45
+
46
+ ##
47
+ # Read a length prefix from an RLP string.
48
+ #
49
+ # * `rlp` - the rlp string to read from
50
+ # * `start` - the position at which to start reading
51
+ #
52
+ # Returns an array `[type, length, end]`, where `type` is either `:str`
53
+ # or `:list` depending on the type of the following payload, `length` is
54
+ # the length of the payload in bytes, and `end` is the position of the
55
+ # first payload byte in the rlp string (thus the end of length prefix).
56
+ #
57
+ def consume_length_prefix(rlp, start)
58
+ b0 = rlp[start].ord
59
+
60
+ if b0 < PRIMITIVE_PREFIX_OFFSET # single byte
61
+ [:str, 1, start]
62
+ elsif b0 < PRIMITIVE_PREFIX_OFFSET + SHORT_LENGTH_LIMIT # short string
63
+ [:str, b0 - PRIMITIVE_PREFIX_OFFSET, start + 1]
64
+ elsif b0 < LIST_PREFIX_OFFSET # long string
65
+ ll = b0 - PRIMITIVE_PREFIX_OFFSET - SHORT_LENGTH_LIMIT + 1
66
+ l = big_endian_to_int rlp[(start+1)...(start+1+ll)]
67
+ [:str, l, start+1+ll]
68
+ elsif b0 < LIST_PREFIX_OFFSET + SHORT_LENGTH_LIMIT # short list
69
+ [:list, b0 - LIST_PREFIX_OFFSET, start + 1]
70
+ else # long list
71
+ ll = b0 - LIST_PREFIX_OFFSET - SHORT_LENGTH_LIMIT + 1
72
+ l = big_endian_to_int rlp[(start+1)...(start+1+ll)]
73
+ [:list, l, start+1+ll]
74
+ end
75
+ end
76
+
77
+ extend self
78
+ end
79
+ end
80
+
81
+ # TODO: benchmark
@@ -0,0 +1,625 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ require 'ethereum/fast_vm/call_data'
4
+ require 'ethereum/fast_vm/message'
5
+ require 'ethereum/fast_vm/state'
6
+
7
+ module Ethereum
8
+ class FastVM
9
+
10
+ STACK_MAX = 1024
11
+
12
+ START_BREAKPOINTS = %i(JUMPDEST GAS PC).freeze
13
+ END_BREAKPOINTS = %i(JUMP JUMPI CALL CALLCODE CREATE SUICIDE STOP RETURN INVALID GAS PC)
14
+
15
+ include Constant
16
+
17
+ OP_INVALID = -1
18
+ Opcodes::TABLE.each do |code, defn|
19
+ const_set "OP_#{defn[0]}", code
20
+ end
21
+
22
+ class <<self
23
+ def code_cache
24
+ @code_cache ||= {}
25
+ end
26
+
27
+ def execute(*args)
28
+ new.execute(*args)
29
+ end
30
+ end
31
+
32
+ def execute(ext, msg, code)
33
+ s = State.new gas: msg.gas
34
+
35
+ if VM.code_cache.has_key?(code)
36
+ processed_code = VM.code_cache[code]
37
+ else
38
+ processed_code = preprocess_code code
39
+ VM.code_cache[code] = processed_code
40
+ end
41
+
42
+ # for trace only
43
+ steps = 0
44
+ _prevop = nil
45
+
46
+ timestamp = Time.now
47
+ loop do
48
+ return vm_exception('INVALID START POINT') if processed_code.has_key?(s.pc)
49
+
50
+ cc = processed_code[s.pc]
51
+ gas, min_stack, max_stack, s.pc = cc[0,4]
52
+ ops = cc[4..-1]
53
+
54
+ return vm_exception('OUT OF GAS') if gas > s.gas
55
+ 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
56
+
57
+ s.gas -= gas
58
+
59
+ ops.each do |op|
60
+ if Logger.trace?(log_vm_exit.name)
61
+ trace_data = {
62
+ stack: s.stack.map(&:to_s),
63
+ inst: op,
64
+ pc: s.pc-1,
65
+ op: op,
66
+ steps: steps
67
+ }
68
+
69
+ if [OP_MLOAD, OP_MSTORE, OP_MSTORE8, OP_SHA3, OP_CALL, OP_CALLCODE, OP_CREATE, OP_CALLDATACOPY, OP_CODECOPY, OP_EXTCODECOPY].include?(_prevop)
70
+ if s.memory.size < 1024
71
+ trace_data[:memory] = Utils.encode_hex(Utils.int_array_to_bytes(s.memory))
72
+ else
73
+ trace_data[:sha3memory] = Utils.encode_hex(Utils.keccak256(Utils.int_array_to_bytes(s.memory)))
74
+ end
75
+ end
76
+
77
+ if [OP_SSTORE, OP_SLOAD].include?(_prevop) || steps == 0
78
+ trace_data[:storage] = ext.log_storage(msg.to)
79
+ end
80
+
81
+ if steps == 0
82
+ trace_data[:depth] = msg.depth
83
+ trace_data[:address] = msg.to
84
+ end
85
+
86
+ log_vm_op.trace('vm', **trace_data)
87
+
88
+ steps += 1
89
+ _prevop = op
90
+ end
91
+
92
+ # Invalid operation
93
+ return vm_exception('INVALID OP', op: op) if op == OP_INVALID
94
+
95
+ # Valid operations
96
+ stk = s.stack
97
+ mem = s.memory
98
+ if op < 0x10 # Stop & Arithmetic Operations
99
+ case op
100
+ when OP_STOP
101
+ return peaceful_exit('STOP', s.gas, [])
102
+ when OP_ADD
103
+ r = (stk.pop + stk.pop) & UINT_MAX
104
+ stk.push r
105
+ when OP_SUB
106
+ r = (stk.pop - stk.pop) & UINT_MAX
107
+ stk.push r
108
+ when OP_MUL
109
+ r = (stk.pop * stk.pop) & UINT_MAX
110
+ stk.push r
111
+ when OP_DIV
112
+ s0, s1 = stk.pop, stk.pop
113
+ stk.push(s1 == 0 ? 0 : s0 / s1)
114
+ when OP_MOD
115
+ s0, s1 = stk.pop, stk.pop
116
+ stk.push(s1 == 0 ? 0 : s0 % s1)
117
+ when OP_SDIV
118
+ s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
119
+ r = s1 == 0 ? 0 : ((s0.abs / s1.abs * (s0*s1 < 0 ? -1 : 1)) & UINT_MAX)
120
+ stk.push r
121
+ when OP_SMOD
122
+ s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
123
+ r = s1 == 0 ? 0 : ((s0.abs % s1.abs * (s0 < 0 ? -1 : 1)) & UINT_MAX)
124
+ stk.push r
125
+ when OP_ADDMOD
126
+ s0, s1, s2 = stk.pop, stk.pop, stk.pop
127
+ r = s2 == 0 ? 0 : (s0+s1) % s2
128
+ stk.push r
129
+ when OP_MULMOD
130
+ s0, s1, s2 = stk.pop, stk.pop, stk.pop
131
+ r = s2 == 0 ? 0 : Utils.mod_mul(s0, s1, s2)
132
+ stk.push r
133
+ when OP_EXP
134
+ base, exponent = stk.pop, stk.pop
135
+
136
+ # fee for exponent is dependent on its bytes
137
+ # calc n bytes to represent exponent
138
+ nbytes = Utils.encode_int(exponent).size
139
+ expfee = nbytes * Opcodes::GEXPONENTBYTE
140
+ if s.gas < expfee
141
+ s.gas = 0
142
+ return vm_exception('OOG EXPONENT')
143
+ end
144
+
145
+ s.gas -= expfee
146
+ stk.push Utils.mod_exp(base, exponent, TT256)
147
+ when OP_SIGNEXTEND # extend sign from bytes at s0 to left
148
+ s0, s1 = stk.pop, stk.pop
149
+ if s0 < 32
150
+ testbit = s0*8 + 7
151
+ mask = 1 << testbit
152
+ if s1 & mask == 0 # extend 0s
153
+ stk.push(s1 & (mask - 1))
154
+ else # extend 1s
155
+ stk.push(s1 | (TT256 - mask))
156
+ end
157
+ else
158
+ stk.push s1
159
+ end
160
+ end
161
+ elsif op < 0x20 # Comparison & Bitwise Logic Operations
162
+ case op
163
+ when OP_LT
164
+ s0, s1 = stk.pop, stk.pop
165
+ stk.push(s0 < s1 ? 1 : 0)
166
+ when OP_GT
167
+ s0, s1 = stk.pop, stk.pop
168
+ stk.push(s0 > s1 ? 1 : 0)
169
+ when OP_SLT
170
+ s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
171
+ stk.push(s0 < s1 ? 1 : 0)
172
+ when OP_SGT
173
+ s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
174
+ stk.push(s0 > s1 ? 1 : 0)
175
+ when OP_EQ
176
+ s0, s1 = stk.pop, stk.pop
177
+ stk.push(s0 == s1 ? 1 : 0)
178
+ when OP_ISZERO
179
+ s0 = stk.pop
180
+ stk.push(s0 == 0 ? 1 : 0)
181
+ when OP_AND
182
+ s0, s1 = stk.pop, stk.pop
183
+ stk.push(s0 & s1)
184
+ when OP_OR
185
+ s0, s1 = stk.pop, stk.pop
186
+ stk.push(s0 | s1)
187
+ when OP_XOR
188
+ s0, s1 = stk.pop, stk.pop
189
+ stk.push(s0 ^ s1)
190
+ when OP_NOT
191
+ s0 = stk.pop
192
+ stk.push(UINT_MAX - s0)
193
+ when OP_BYTE
194
+ s0, s1 = stk.pop, stk.pop
195
+ if s0 < 32
196
+ stk.push((s1 / 256**(31-s0)) % 256)
197
+ else
198
+ stk.push(0)
199
+ end
200
+ end
201
+ elsif op < 0x40 # SHA3 & Environmental Information
202
+ case op
203
+ when OP_SHA3
204
+ s0, s1 = stk.pop, stk.pop
205
+
206
+ s.gas -= Opcodes::GSHA3WORD * (Utils.ceil32(s1) / 32)
207
+ return vm_exception('OOG PAYING FOR SHA3') if s.gas < 0
208
+
209
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
210
+
211
+ data = Utils.int_array_to_bytes mem.safe_slice(s0,s1)
212
+ stk.push Utils.big_endian_to_int(Utils.keccak256(data))
213
+ when OP_ADDRESS
214
+ stk.push Utils.coerce_to_int(msg.to)
215
+ when OP_BALANCE
216
+ s0 = stk.pop
217
+ addr = Utils.coerce_addr_to_hex(s0 % 2**160)
218
+ stk.push ext.get_balance(addr)
219
+ when OP_ORIGIN
220
+ stk.push Utils.coerce_to_int(ext.tx_origin)
221
+ when OP_CALLER
222
+ stk.push Utils.coerce_to_int(msg.sender)
223
+ when OP_CALLVALUE
224
+ stk.push msg.value
225
+ when OP_CALLDATALOAD
226
+ stk.push msg.data.extract32(stk.pop)
227
+ when OP_CALLDATASIZE
228
+ stk.push msg.data.size
229
+ when OP_CALLDATACOPY
230
+ mstart, dstart, size = stk.pop, stk.pop, stk.pop
231
+
232
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
233
+ return vm_exception('OOG COPY DATA') unless data_copy(s, size)
234
+
235
+ msg.data.extract_copy(mem, mstart, dstart, size)
236
+ when OP_CODESIZE
237
+ stk.push code.size
238
+ when OP_CODECOPY
239
+ mstart, cstart, size = stk.pop, stk.pop, stk.pop
240
+
241
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
242
+ return vm_exception('OOG COPY CODE') unless data_copy(s, size)
243
+
244
+ size.times do |i|
245
+ if cstart + i < code.size
246
+ mem[mstart+i] = code[cstart+i].ord
247
+ else
248
+ mem[mstart+i] = 0
249
+ end
250
+ end
251
+ when OP_GASPRICE
252
+ stk.push ext.tx_gasprice
253
+ when OP_EXTCODESIZE
254
+ addr = stk.pop
255
+ addr = Utils.coerce_addr_to_hex(addr % 2**160)
256
+ stk.push (ext.get_code(addr) || Constant::BYTE_EMPTY).size
257
+ when OP_EXTCODECOPY
258
+ addr, mstart, cstart, size = stk.pop, stk.pop, stk.pop, stk.pop
259
+ addr = Utils.coerce_addr_to_hex(addr % 2**160)
260
+ extcode = ext.get_code(addr) || Constant::BYTE_EMPTY
261
+ raise ValueError, "extcode must be string" unless extcode.is_a?(String)
262
+
263
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
264
+ return vm_exception('OOG COPY CODE') unless data_copy(s, size)
265
+
266
+ size.times do |i|
267
+ if cstart + i < extcode.size
268
+ mem[mstart+i] = extcode[cstart+i].ord
269
+ else
270
+ mem[mstart+i] = 0
271
+ end
272
+ end
273
+ end
274
+ elsif op < 0x50 # Block Information
275
+ case op
276
+ when OP_BLOCKHASH
277
+ s0 = stk.pop
278
+ stk.push Utils.big_endian_to_int(ext.block_hash(s0))
279
+ when OP_COINBASE
280
+ stk.push Utils.big_endian_to_int(ext.block_coinbase)
281
+ when OP_TIMESTAMP
282
+ stk.push ext.block_timestamp
283
+ when OP_NUMBER
284
+ stk.push ext.block_number
285
+ when OP_DIFFICULTY
286
+ stk.push ext.block_difficulty
287
+ when OP_GASLIMIT
288
+ stk.push ext.block_gas_limit
289
+ end
290
+ elsif op < 0x60 # Stack, Memory, Storage and Flow Operations
291
+ case op
292
+ when OP_POP
293
+ stk.pop
294
+ when OP_MLOAD
295
+ s0 = stk.pop
296
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)
297
+
298
+ data = 0
299
+ mem[s0, 32].each do |c|
300
+ data = (data << 8) + c
301
+ end
302
+
303
+ stk.push data
304
+ when OP_MSTORE
305
+ s0, s1 = stk.pop, stk.pop
306
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)
307
+
308
+ 32.times.to_a.reverse.each do |i|
309
+ mem[s0+i] = s1 % 256
310
+ s1 /= 256
311
+ end
312
+ when OP_MSTORE8
313
+ s0, s1 = stk.pop, stk.pop
314
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 1)
315
+ mem[s0] = s1 % 256
316
+ when OP_SLOAD
317
+ s0 = stk.pop
318
+ stk.push ext.get_storage_data(msg.to, s0)
319
+ when OP_SSTORE
320
+ s0, s1 = stk.pop, stk.pop
321
+
322
+ if ext.get_storage_data(msg.to, s0) != 0
323
+ gascost = s1 == 0 ? Opcodes::GSTORAGEKILL : Opcodes::GSTORAGEMOD
324
+ refund = s1 == 0 ? Opcodes::GSTORAGEREFUND : 0
325
+ else
326
+ gascost = s1 == 0 ? Opcodes::GSTORAGEMOD : Opcodes::GSTORAGEADD
327
+ refund = 0
328
+ end
329
+
330
+ return vm_exception('OUT OF GAS') if s.gas < gascost
331
+
332
+ s.gas -= gascost
333
+ ext.add_refund refund
334
+ ext.set_storage_data msg.to, s0, s1
335
+ when OP_JUMP
336
+ s0 = stk.pop
337
+ s.pc = s0
338
+
339
+ op_new = processed_code.has_key?(s.pc) ? processed_code[s.pc][4] : OP_STOP
340
+ return vm_exception('BAD JUMPDEST') if op_new != OP_JUMPDEST
341
+ when OP_JUMPI
342
+ s0, s1 = stk.pop, stk.pop
343
+ if s1.true?
344
+ s.pc = s0
345
+ op_new = processed_code.has_key?(s.pc) ? processed_code[s.pc][4] : OP_STOP
346
+ return vm_exception('BAD JUMPDEST') if op_new != OP_JUMPDEST
347
+ end
348
+ when OP_PC
349
+ stk.push(s.pc - 1)
350
+ when OP_MSIZE
351
+ stk.push mem.size
352
+ when OP_GAS
353
+ stk.push s.gas # AFTER subtracting cost 1
354
+ end
355
+ elsif (op & 0xff) >= OP_PUSH1 && (op & 0xff) <= OP_PUSH32
356
+ stk.push(op >> 8)
357
+ elsif op >= OP_DUP1 && op <= OP_DUP16
358
+ depth = op - OP_DUP1 + 1
359
+ stk.push stk[-depth]
360
+ elsif op >= OP_SWAP1 && op <= OP_SWAP16
361
+ depth = op - OP_SWAP1 + 1
362
+ temp = stk[-depth - 1]
363
+ stk[-depth - 1] = stk[-1]
364
+ stk[-1] = temp
365
+ elsif op >= OP_LOG0 && op <= OP_LOG4
366
+ # 0xa0 ... 0xa4, 32/64/96/128/160 + data.size gas
367
+ #
368
+ # a. Opcodes LOG0...LOG4 are added, takes 2-6 stake arguments:
369
+ # MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
370
+ #
371
+ # b. Logs are kept track of during tx execution exactly the same way
372
+ # as suicides (except as an ordered list, not a set).
373
+ #
374
+ # Each log is in the form [address, [topic1, ... ], data] where:
375
+ # * address is what the ADDRESS opcode would output
376
+ # * data is mem[MEMSTART, MEMSZ]
377
+ # * topics are as provided by the opcode
378
+ #
379
+ # c. The ordered list of logs in the transation are expreseed as
380
+ # [log0, log1, ..., logN].
381
+ #
382
+ depth = op - OP_LOG0
383
+ mstart, msz = stk.pop, stk.pop
384
+ topics = depth.times.map {|i| stk.pop }
385
+
386
+ s.gas -= msz * Opcodes::GLOGBYTE
387
+
388
+ return vm_exception("OOG EXTENDING MEMORY") unless mem_extend(mem, s, mstart, msz)
389
+
390
+ data = mem.safe_slice(mstart, msz)
391
+ ext.log(msg.to, topics, Utils.int_array_to_bytes(data))
392
+ log_log.trace('LOG', to: msg.to, topics: topics, data: data)
393
+ elsif op == OP_CREATE
394
+ value, mstart, msz = stk.pop, stk.pop, stk.pop
395
+
396
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, msz)
397
+
398
+ if ext.get_balance(msg.to) >= value && msg.depth < 1024
399
+ cd = CallData.new mem, mstart, msz
400
+ create_msg = Message.new(msg.to, Constant::BYTE_EMPTY, value, s.gas, cd, depth: msg.depth+1)
401
+
402
+ o, gas, addr = ext.create create_msg
403
+ if o.true?
404
+ stk.push Utils.coerce_to_int(addr)
405
+ s.gas = gas
406
+ else
407
+ stk.push 0
408
+ s.gas = 0
409
+ end
410
+ else
411
+ stk.push(0)
412
+ end
413
+ elsif op == OP_CALL
414
+ gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
415
+ stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
416
+
417
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
418
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
419
+
420
+ to = Utils.zpad_int(to)[12..-1] # last 20 bytes
421
+ extra_gas = (ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT +
422
+ (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
423
+ submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
424
+ total_gas = gas + extra_gas
425
+
426
+ return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
427
+
428
+ if ext.get_balance(msg.to) >= value && msg.depth < 1024
429
+ s.gas -= total_gas
430
+
431
+ cd = CallData.new mem, memin_start, memin_sz
432
+ call_msg = Message.new(msg.to, to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
433
+
434
+ result, gas, data = ext.apply_msg call_msg
435
+ if result == 0
436
+ stk.push 0
437
+ else
438
+ stk.push 1
439
+ s.gas += gas
440
+ [data.size, memout_sz].min.times do |i|
441
+ mem[memout_start+i] = data[i]
442
+ end
443
+ end
444
+ else
445
+ s.gas -= (total_gas - submsg_gas)
446
+ stk.push(0)
447
+ end
448
+ elsif op == OP_CALLCODE
449
+ gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
450
+ stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
451
+
452
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
453
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
454
+
455
+ extra_gas = (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
456
+ submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
457
+ total_gas = gas + extra_gas
458
+
459
+ return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
460
+
461
+ if ext.get_balance(msg.to) >= value && msg.depth < 1024
462
+ s.gas -= total_gas
463
+
464
+ to = Utils.zpad_int(to)[12..-1] # last 20 bytes
465
+ cd = CallData.new mem, memin_start, memin_sz
466
+
467
+ call_msg = Message.new(msg.to, msg.to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
468
+
469
+ result, gas, data = ext.apply_msg call_msg
470
+ if result == 0
471
+ stk.push 0
472
+ else
473
+ stk.push 1
474
+ s.gas += gas
475
+ [data.size, memout_sz].min.times do |i|
476
+ mem[memout_start+i] = data[i]
477
+ end
478
+ end
479
+ else
480
+ s.gas -= (total_gas - submsg_gas)
481
+ stk.push(0)
482
+ end
483
+ elsif op == OP_RETURN
484
+ s0, s1 = stk.pop, stk.pop
485
+ return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
486
+ return peaceful_exit('RETURN', s.gas, mem.safe_slice(s0, s1))
487
+ elsif op == OP_SUICIDE
488
+ s0 = stk.pop
489
+ to = Utils.zpad_int(s0)[12..-1] # last 20 bytes
490
+
491
+ xfer = ext.get_balance(msg.to)
492
+ ext.set_balance(to, ext.get_balance(to)+xfer)
493
+ ext.set_balance(msg.to, 0)
494
+ ext.add_suicide(msg.to)
495
+
496
+ return 1, s.gas, []
497
+ end
498
+
499
+
500
+ end
501
+ end
502
+ end
503
+
504
+ def preprocess_code(code)
505
+ code = Utils.bytes_to_int_array code
506
+
507
+ ops = {}
508
+ cur_chunk = []
509
+ cc_init_pos = 0 # cc = code chunk
510
+ cc_gas_consumption = 0
511
+ cc_stack_change = 0
512
+ cc_min_req_stack = 0 # minimum stack depth before code chunk start
513
+ cc_max_req_stack = STACK_MAX # maximum stack depth before code chunk start
514
+
515
+ i = 0
516
+ while i < code.size
517
+ op, in_args, out_args, fee = Opcodes::TABLE.fetch(code[i], [:INVALID, 0, 0, 0])
518
+ opcode, pushval = code[i], 0
519
+
520
+ if op[0,Opcodes::PREFIX_PUSH.size] == Opcodes::PREFIX_PUSH
521
+ n = op[Opcodes::PREFIX_PUSH.size..-1].to_i
522
+ n.times do |j|
523
+ i += 1
524
+ byte = i < code.size ? code[i] : 0
525
+ pushval = (pushval << 8) + byte
526
+ end
527
+ end
528
+
529
+ i += 1
530
+
531
+ opcode = OP_INVALID if op == :INVALID
532
+
533
+ cc_gas_consumption += fee
534
+ cc_min_req_stack = [cc_min_req_stack, 0 - cc_stack_change + in_args].max # should leave at least in_args values in stack as arguments of this op
535
+ cc_max_req_stack = [cc_max_req_stack, STACK_MAX - cc_stack_change + in_args - out_args].min # should leave enough stack space for code chunk use
536
+ cc_stack_change = cc_stack_change - in_args + out_args
537
+ cur_chunk.push(opcode + (pushval << 8))
538
+
539
+ if END_BREAKPOINTS.include?(op) || i >= code.size ||
540
+ START_BREAKPOINTS.include?(Opcodes::TABLE.fetch(code[i], [:INVALID])[0])
541
+ ops[cc_init_pos] = [
542
+ cc_gas_consumption,
543
+ cc_min_req_stack,
544
+ cc_max_req_stack,
545
+ i # end position
546
+ ] + cur_chunk
547
+
548
+ cur_chunk = []
549
+ cc_init_pos = i
550
+ cc_gas_consumption = 0
551
+ cc_stack_change = 0
552
+ cc_min_req_stack = 0
553
+ cc_max_req_stack = STACK_MAX
554
+ end
555
+ end
556
+
557
+ ops[i] = [0, 0, STACK_MAX, [0], 0]
558
+ ops
559
+ end
560
+
561
+ private
562
+
563
+ def log_vm_exit
564
+ @log_vm_exit ||= Logger.new 'eth.vm.exit'
565
+ end
566
+
567
+ def log_vm_op
568
+ @log_vm_op ||= Logger.new 'eth.vm.op'
569
+ end
570
+
571
+ def log_log
572
+ @log_log ||= Logger.new 'eth.vm.log'
573
+ end
574
+
575
+ def vm_exception(error, **kwargs)
576
+ log_vm_exit.trace('EXCEPTION', cause: error, **kwargs)
577
+ return 0, 0, []
578
+ end
579
+
580
+ def peaceful_exit(cause, gas, data, **kwargs)
581
+ log_vm_exit.trace('EXIT', cause: cause, **kwargs)
582
+ return 1, gas, data
583
+ end
584
+
585
+ def mem_extend(mem, s, start, sz)
586
+ if sz > 0 && Utils.ceil32(start + sz) > mem.size
587
+ oldsize = mem.size / 32
588
+ old_totalfee = mem_fee oldsize
589
+
590
+ newsize = Utils.ceil32(start + sz) / 32
591
+ new_totalfee = mem_fee newsize
592
+
593
+ if old_totalfee < new_totalfee
594
+ memfee = new_totalfee - old_totalfee
595
+
596
+ if s.gas < memfee
597
+ s.gas = 0
598
+ return false
599
+ end
600
+ s.gas -= memfee
601
+
602
+ m_extend = (newsize - oldsize) * 32
603
+ mem.concat([0]*m_extend)
604
+ end
605
+ end
606
+
607
+ true
608
+ end
609
+
610
+ def data_copy(s, sz)
611
+ if sz > 0
612
+ copyfee = Opcodes::GCOPY * Utils.ceil32(sz) / 32
613
+
614
+ if s.gas < copyfee
615
+ s.gas = 0
616
+ return false
617
+ end
618
+ s.gas -= copyfee
619
+ end
620
+
621
+ true
622
+ end
623
+
624
+ end
625
+ end