ruby-ethereum 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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