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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +40 -0
- data/lib/ethereum.rb +53 -0
- data/lib/ethereum/abi.rb +333 -0
- data/lib/ethereum/abi/contract_translator.rb +174 -0
- data/lib/ethereum/abi/type.rb +117 -0
- data/lib/ethereum/account.rb +72 -0
- data/lib/ethereum/address.rb +60 -0
- data/lib/ethereum/base_convert.rb +53 -0
- data/lib/ethereum/block.rb +1311 -0
- data/lib/ethereum/block_header.rb +211 -0
- data/lib/ethereum/bloom.rb +83 -0
- data/lib/ethereum/cached_block.rb +36 -0
- data/lib/ethereum/chain.rb +400 -0
- data/lib/ethereum/constant.rb +26 -0
- data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
- data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
- data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
- data/lib/ethereum/core_ext/object/truth.rb +47 -0
- data/lib/ethereum/db.rb +6 -0
- data/lib/ethereum/db/base_db.rb +9 -0
- data/lib/ethereum/db/ephem_db.rb +63 -0
- data/lib/ethereum/db/overlay_db.rb +72 -0
- data/lib/ethereum/db/refcount_db.rb +188 -0
- data/lib/ethereum/env.rb +64 -0
- data/lib/ethereum/ethash.rb +78 -0
- data/lib/ethereum/ethash_ruby.rb +38 -0
- data/lib/ethereum/ethash_ruby/cache.rb +47 -0
- data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
- data/lib/ethereum/ethash_ruby/utils.rb +53 -0
- data/lib/ethereum/exceptions.rb +28 -0
- data/lib/ethereum/external_call.rb +173 -0
- data/lib/ethereum/fast_rlp.rb +81 -0
- data/lib/ethereum/fast_vm.rb +625 -0
- data/lib/ethereum/fast_vm/call_data.rb +44 -0
- data/lib/ethereum/fast_vm/message.rb +29 -0
- data/lib/ethereum/fast_vm/state.rb +25 -0
- data/lib/ethereum/ffi/openssl.rb +390 -0
- data/lib/ethereum/index.rb +97 -0
- data/lib/ethereum/log.rb +43 -0
- data/lib/ethereum/miner.rb +84 -0
- data/lib/ethereum/opcodes.rb +131 -0
- data/lib/ethereum/private_key.rb +92 -0
- data/lib/ethereum/pruning_trie.rb +28 -0
- data/lib/ethereum/public_key.rb +88 -0
- data/lib/ethereum/receipt.rb +53 -0
- data/lib/ethereum/secp256k1.rb +58 -0
- data/lib/ethereum/secure_trie.rb +49 -0
- data/lib/ethereum/sedes.rb +42 -0
- data/lib/ethereum/special_contract.rb +95 -0
- data/lib/ethereum/spv.rb +79 -0
- data/lib/ethereum/spv/proof.rb +31 -0
- data/lib/ethereum/spv/proof_constructor.rb +19 -0
- data/lib/ethereum/spv/proof_verifier.rb +24 -0
- data/lib/ethereum/tester.rb +14 -0
- data/lib/ethereum/tester/abi_contract.rb +65 -0
- data/lib/ethereum/tester/fixture.rb +31 -0
- data/lib/ethereum/tester/language.rb +30 -0
- data/lib/ethereum/tester/log_recorder.rb +13 -0
- data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
- data/lib/ethereum/tester/state.rb +194 -0
- data/lib/ethereum/transaction.rb +196 -0
- data/lib/ethereum/transient_trie.rb +28 -0
- data/lib/ethereum/trie.rb +549 -0
- data/lib/ethereum/trie/nibble_key.rb +184 -0
- data/lib/ethereum/utils.rb +191 -0
- data/lib/ethereum/version.rb +5 -0
- data/lib/ethereum/vm.rb +606 -0
- data/lib/ethereum/vm/call_data.rb +40 -0
- data/lib/ethereum/vm/message.rb +30 -0
- data/lib/ethereum/vm/state.rb +25 -0
- 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
|