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,184 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
module Ethereum
|
4
|
+
class Trie
|
5
|
+
|
6
|
+
##
|
7
|
+
# Nibble is half-byte.
|
8
|
+
#
|
9
|
+
class NibbleKey < Array
|
10
|
+
|
11
|
+
NIBBLE_TERM_FLAG = 0b0010
|
12
|
+
NIBBLE_ODD_FLAG = 0b0001
|
13
|
+
NIBBLE_TERMINATOR = 16
|
14
|
+
|
15
|
+
HEX_VALUES = (0..15).inject({}) {|h, i| h[i.to_s(16)] = i; h}.freeze
|
16
|
+
|
17
|
+
class <<self
|
18
|
+
##
|
19
|
+
# Encode nibbles to string.
|
20
|
+
#
|
21
|
+
# @see `pack_nibbles` in pyethereum
|
22
|
+
#
|
23
|
+
# @param nibbles [Array[Integer]] array of nibbles to encode
|
24
|
+
#
|
25
|
+
# @return [String] encoded string
|
26
|
+
#
|
27
|
+
def encode(nibbles)
|
28
|
+
flags = 0
|
29
|
+
|
30
|
+
if nibbles.last == NIBBLE_TERMINATOR
|
31
|
+
flags |= NIBBLE_TERM_FLAG
|
32
|
+
nibbles = nibbles[0...-1]
|
33
|
+
end
|
34
|
+
|
35
|
+
odd = nibbles.size % 2
|
36
|
+
flags |= odd
|
37
|
+
if odd == 1
|
38
|
+
nibbles = [flags] + nibbles
|
39
|
+
else
|
40
|
+
nibbles = [flags, 0b0000] + nibbles
|
41
|
+
end
|
42
|
+
|
43
|
+
(nibbles.size/2).times.reduce('') do |s, i|
|
44
|
+
base = 2*i
|
45
|
+
s += (16*nibbles[base] + nibbles[base+1]).chr
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
##
|
50
|
+
# Decode bytes to {NibbleKey}, with flags processed.
|
51
|
+
#
|
52
|
+
# @see `unpack_to_nibbles` in pyethereum
|
53
|
+
#
|
54
|
+
# @param bytes [String] compact hex encoded string.
|
55
|
+
#
|
56
|
+
# @return [NibbleKey] nibbles array, may have a terminator
|
57
|
+
#
|
58
|
+
def decode(bytes)
|
59
|
+
o = from_string bytes
|
60
|
+
flags = o[0]
|
61
|
+
|
62
|
+
o.push NIBBLE_TERMINATOR if flags & NIBBLE_TERM_FLAG > 0
|
63
|
+
fill = flags & NIBBLE_ODD_FLAG > 0 ? 1 : 2
|
64
|
+
|
65
|
+
new o[fill..-1]
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# Convert arbitrary string to {NibbleKey}.
|
70
|
+
#
|
71
|
+
# @see `bin_to_nibbles` in pyethereum
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# from_string('') # => []
|
75
|
+
# from_string('h') # => [6, 8]
|
76
|
+
# from_string('he') # => [6, 8, 6, 5]
|
77
|
+
# from_string('hello') # => [6, 8, 6, 5, 6, 12, 6, 12, 6, 15]
|
78
|
+
#
|
79
|
+
# @param s [String] any string
|
80
|
+
#
|
81
|
+
# @return [NibbleKey] array of nibbles presented as interger smaller
|
82
|
+
# than 16, has no terminator because plain string has no flags
|
83
|
+
#
|
84
|
+
def from_string(s)
|
85
|
+
nibbles = RLP::Utils.encode_hex(s).each_char.map {|nibble| HEX_VALUES[nibble] }
|
86
|
+
new nibbles
|
87
|
+
end
|
88
|
+
|
89
|
+
##
|
90
|
+
# Convert array of nibbles to string.
|
91
|
+
#
|
92
|
+
# @see `nibbles_to_bin` in pyethereum
|
93
|
+
#
|
94
|
+
# @param nibbles [Array] array of nibbles
|
95
|
+
#
|
96
|
+
# @return [String] string represented by nibbles
|
97
|
+
#
|
98
|
+
def to_string(nibbles)
|
99
|
+
raise ArgumentError, "nibbles can only be in 0..15" if nibbles.any? {|x| x > 15 || x < 0 }
|
100
|
+
raise ArgumentError, "nibbles must be of even numbers" if nibbles.size % 2 == 1
|
101
|
+
|
102
|
+
(nibbles.size/2).times.map do |i|
|
103
|
+
base = i*2
|
104
|
+
(16*nibbles[base] + nibbles[base+1]).chr
|
105
|
+
end.join
|
106
|
+
end
|
107
|
+
|
108
|
+
def terminator
|
109
|
+
new([NIBBLE_TERMINATOR])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def initialize(*args)
|
114
|
+
super
|
115
|
+
end
|
116
|
+
|
117
|
+
def terminate?
|
118
|
+
last == NIBBLE_TERMINATOR
|
119
|
+
end
|
120
|
+
|
121
|
+
##
|
122
|
+
# Get with or without terminator copy of this {NibbleKey}.
|
123
|
+
#
|
124
|
+
# @param flag [Bool] set true to get a copy with terminator, otherwise
|
125
|
+
# set false
|
126
|
+
#
|
127
|
+
# @return [NibbleKey] a copy with or without terminator at end
|
128
|
+
#
|
129
|
+
def terminate(flag)
|
130
|
+
dup.tap do |copy|
|
131
|
+
if flag
|
132
|
+
copy.push NIBBLE_TERMINATOR unless copy.terminate?
|
133
|
+
else
|
134
|
+
copy.pop if copy.terminate?
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# test whether this is prefix of another {NibbleKey}
|
141
|
+
#
|
142
|
+
# @param another_key [NibbleKey] the full key to test
|
143
|
+
#
|
144
|
+
# @return [Bool]
|
145
|
+
#
|
146
|
+
def prefix?(another_key)
|
147
|
+
return false if another_key.size < size
|
148
|
+
another_key.take(size) == self
|
149
|
+
end
|
150
|
+
|
151
|
+
##
|
152
|
+
# Find common prefix to another key.
|
153
|
+
#
|
154
|
+
# @param another_key [Array] another array of nibbles
|
155
|
+
#
|
156
|
+
# @return [Array] common prefix of both nibbles array
|
157
|
+
#
|
158
|
+
def common_prefix(another_key)
|
159
|
+
prefix = []
|
160
|
+
|
161
|
+
[size, another_key.size].min.times do |i|
|
162
|
+
break if self[i] != another_key[i]
|
163
|
+
prefix.push self[i]
|
164
|
+
end
|
165
|
+
|
166
|
+
self.class.new prefix
|
167
|
+
end
|
168
|
+
|
169
|
+
def encode
|
170
|
+
self.class.encode self
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_string
|
174
|
+
self.class.to_string self
|
175
|
+
end
|
176
|
+
|
177
|
+
def +(array)
|
178
|
+
self.class.new(super(array))
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
require 'digest/sha3'
|
5
|
+
require 'openssl'
|
6
|
+
|
7
|
+
module Ethereum
|
8
|
+
module Utils
|
9
|
+
|
10
|
+
extend self
|
11
|
+
|
12
|
+
include Constant
|
13
|
+
|
14
|
+
##
|
15
|
+
# Not the keccak in sha3, although it's underlying lib named SHA3
|
16
|
+
#
|
17
|
+
def keccak256(x)
|
18
|
+
Digest::SHA3.new(256).digest(x)
|
19
|
+
end
|
20
|
+
|
21
|
+
def keccak512(x)
|
22
|
+
Digest::SHA3.new(512).digest(x)
|
23
|
+
end
|
24
|
+
|
25
|
+
def keccak256_rlp(x)
|
26
|
+
keccak256 RLP.encode(x)
|
27
|
+
end
|
28
|
+
|
29
|
+
def sha256(x)
|
30
|
+
Digest::SHA256.digest x
|
31
|
+
end
|
32
|
+
|
33
|
+
def double_sha256(x)
|
34
|
+
sha256 sha256(x)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ripemd160(x)
|
38
|
+
Digest::RMD160.digest x
|
39
|
+
end
|
40
|
+
|
41
|
+
def hash160(x)
|
42
|
+
ripemd160 sha256(x)
|
43
|
+
end
|
44
|
+
|
45
|
+
def hash160_hex(x)
|
46
|
+
encode_hex hash160(x)
|
47
|
+
end
|
48
|
+
|
49
|
+
def mod_exp(x, y, n)
|
50
|
+
x.to_bn.mod_exp(y, n).to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def mod_mul(x, y, n)
|
54
|
+
x.to_bn.mod_mul(y, n).to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_signed(i)
|
58
|
+
i > Constant::INT_MAX ? (i-Constant::TT256) : i
|
59
|
+
end
|
60
|
+
|
61
|
+
def base58_check_to_bytes(s)
|
62
|
+
leadingzbytes = s.match(/\A1*/)[0]
|
63
|
+
data = Constant::BYTE_ZERO * leadingzbytes.size + BaseConvert.convert(s, 58, 256)
|
64
|
+
|
65
|
+
raise ChecksumError, "double sha256 checksum doesn't match" unless double_sha256(data[0...-4])[0,4] == data[-4..-1]
|
66
|
+
data[1...-4]
|
67
|
+
end
|
68
|
+
|
69
|
+
def bytes_to_base58_check(bytes, magicbyte=0)
|
70
|
+
bs = "#{magicbyte.chr}#{bytes}"
|
71
|
+
leadingzbytes = bs.match(/\A#{Constant::BYTE_ZERO}*/)[0]
|
72
|
+
checksum = double_sha256(bs)[0,4]
|
73
|
+
'1'*leadingzbytes.size + BaseConvert.convert("#{bs}#{checksum}", 256, 58)
|
74
|
+
end
|
75
|
+
|
76
|
+
def ceil32(x)
|
77
|
+
x % 32 == 0 ? x : (x + 32 - x%32)
|
78
|
+
end
|
79
|
+
|
80
|
+
def encode_hex(b)
|
81
|
+
RLP::Utils.encode_hex b
|
82
|
+
end
|
83
|
+
|
84
|
+
def decode_hex(s)
|
85
|
+
RLP::Utils.decode_hex s
|
86
|
+
end
|
87
|
+
|
88
|
+
def big_endian_to_int(s)
|
89
|
+
RLP::Sedes.big_endian_int.deserialize s.sub(/\A(\x00)+/, '')
|
90
|
+
end
|
91
|
+
|
92
|
+
def int_to_big_endian(n)
|
93
|
+
RLP::Sedes.big_endian_int.serialize n
|
94
|
+
end
|
95
|
+
|
96
|
+
def lpad(x, symbol, l)
|
97
|
+
return x if x.size >= l
|
98
|
+
symbol * (l - x.size) + x
|
99
|
+
end
|
100
|
+
|
101
|
+
def rpad(x, symbol, l)
|
102
|
+
return x if x.size >= l
|
103
|
+
x + symbol * (l - x.size)
|
104
|
+
end
|
105
|
+
|
106
|
+
def zpad(x, l)
|
107
|
+
lpad x, BYTE_ZERO, l
|
108
|
+
end
|
109
|
+
|
110
|
+
def zunpad(x)
|
111
|
+
x.sub /\A\x00+/, ''
|
112
|
+
end
|
113
|
+
|
114
|
+
def zpad_int(n, l=32)
|
115
|
+
zpad encode_int(n), l
|
116
|
+
end
|
117
|
+
|
118
|
+
def zpad_hex(s, l=32)
|
119
|
+
zpad decode_hex(s), l
|
120
|
+
end
|
121
|
+
|
122
|
+
def int_to_addr(x)
|
123
|
+
zpad_int x, 20
|
124
|
+
end
|
125
|
+
|
126
|
+
def encode_int(n)
|
127
|
+
raise ArgumentError, "Integer invalid or out of range: #{n}" unless n.is_a?(Integer) && n >= 0 && n <= UINT_MAX
|
128
|
+
int_to_big_endian n
|
129
|
+
end
|
130
|
+
|
131
|
+
def decode_int(v)
|
132
|
+
raise ArgumentError, "No leading zero bytes allowed for integers" if v.size > 0 && (v[0] == Constant::BYTE_ZERO || v[0] == 0)
|
133
|
+
big_endian_to_int v
|
134
|
+
end
|
135
|
+
|
136
|
+
def bytearray_to_int(arr)
|
137
|
+
o = 0
|
138
|
+
arr.each {|x| o = (o << 8) + x }
|
139
|
+
o
|
140
|
+
end
|
141
|
+
|
142
|
+
def int_array_to_bytes(arr)
|
143
|
+
arr.pack('C*')
|
144
|
+
end
|
145
|
+
|
146
|
+
def bytes_to_int_array(bytes)
|
147
|
+
bytes.unpack('C*')
|
148
|
+
end
|
149
|
+
|
150
|
+
def coerce_to_int(x)
|
151
|
+
if x.is_a?(Numeric)
|
152
|
+
x
|
153
|
+
elsif x.size == 40
|
154
|
+
big_endian_to_int decode_hex(x)
|
155
|
+
else
|
156
|
+
big_endian_to_int x
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def coerce_to_bytes(x)
|
161
|
+
if x.is_a?(Numeric)
|
162
|
+
int_to_big_endian x
|
163
|
+
elsif x.size == 40
|
164
|
+
decode_hex(x)
|
165
|
+
else
|
166
|
+
x
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def coerce_addr_to_hex(x)
|
171
|
+
if x.is_a?(Numeric)
|
172
|
+
encode_hex zpad(int_to_big_endian(x), 20)
|
173
|
+
elsif x.size == 40 || x.size == 0
|
174
|
+
x
|
175
|
+
else
|
176
|
+
encode_hex zpad(x, 20)[-20..-1]
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def normalize_address(x, allow_blank: false)
|
181
|
+
address = Address.new(x)
|
182
|
+
raise ValueError, "address is blank" if !allow_blank && address.blank?
|
183
|
+
address.to_bytes
|
184
|
+
end
|
185
|
+
|
186
|
+
def mk_contract_address(sender, nonce)
|
187
|
+
keccak256_rlp([normalize_address(sender), nonce])[12..-1]
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
data/lib/ethereum/vm.rb
ADDED
@@ -0,0 +1,606 @@
|
|
1
|
+
# -*- encoding : ascii-8bit -*-
|
2
|
+
|
3
|
+
require 'ethereum/vm/call_data'
|
4
|
+
require 'ethereum/vm/message'
|
5
|
+
require 'ethereum/vm/state'
|
6
|
+
|
7
|
+
module Ethereum
|
8
|
+
class VM
|
9
|
+
|
10
|
+
class <<self
|
11
|
+
def code_cache
|
12
|
+
@code_cache ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(*args)
|
16
|
+
new.execute(*args)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
include Constant
|
21
|
+
|
22
|
+
def execute(ext, msg, code)
|
23
|
+
s = State.new gas: msg.gas
|
24
|
+
|
25
|
+
if VM.code_cache.has_key?(code)
|
26
|
+
processed_code = VM.code_cache[code]
|
27
|
+
else
|
28
|
+
processed_code = preprocess_code code
|
29
|
+
VM.code_cache[code] = processed_code
|
30
|
+
end
|
31
|
+
|
32
|
+
# for trace only
|
33
|
+
steps = 0
|
34
|
+
_prevop = nil
|
35
|
+
|
36
|
+
timestamp = Time.now
|
37
|
+
loop do
|
38
|
+
return peaceful_exit('CODE OUT OF RANGE', s.gas, []) if s.pc >= processed_code.size
|
39
|
+
|
40
|
+
op, in_args, out_args, fee, opcode, pushval = processed_code[s.pc]
|
41
|
+
|
42
|
+
return vm_exception('OUT OF GAS') if fee > s.gas
|
43
|
+
return vm_exception('INSUFFICIENT STACK', op: op, needed: in_args, available: s.stack.size) if in_args > s.stack.size
|
44
|
+
return vm_exception('STACK SIZE LIMIT EXCEEDED', op: op, pre_height: s.stack.size) if (s.stack.size - in_args + out_args) > 1024
|
45
|
+
|
46
|
+
s.gas -= fee
|
47
|
+
s.pc += 1
|
48
|
+
|
49
|
+
# This diverges from normal logging, as we use the logging namespace
|
50
|
+
# only to decide which features get logged in 'eth.vm.op', i.e.
|
51
|
+
# tracing can not be activated by activating a sub like
|
52
|
+
# 'eth.vm.op.stack'.
|
53
|
+
if Logger.trace?(log_vm_exit.name)
|
54
|
+
trace_data = {
|
55
|
+
stack: s.stack.map(&:to_s),
|
56
|
+
gas: s.gas + fee,
|
57
|
+
inst: opcode,
|
58
|
+
pc: s.pc-1,
|
59
|
+
op: op,
|
60
|
+
steps: steps
|
61
|
+
}
|
62
|
+
|
63
|
+
if %i(MLOAD MSTORE MSTORE8 SHA3 CALL CALLCODE CREATE CALLDATACOPY CODECOPY EXTCODECOPY).include?(_prevop)
|
64
|
+
if s.memory.size < 1024
|
65
|
+
trace_data[:memory] = Utils.encode_hex(Utils.int_array_to_bytes(s.memory))
|
66
|
+
else
|
67
|
+
trace_data[:sha3memory] = Utils.encode_hex(Utils.keccak256(Utils.int_array_to_bytes(s.memory)))
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
if %i(SSTORE SLOAD).include?(_prevop) || steps == 0
|
72
|
+
trace_data[:storage] = ext.log_storage(msg.to)
|
73
|
+
end
|
74
|
+
|
75
|
+
if steps == 0
|
76
|
+
trace_data[:depth] = msg.depth
|
77
|
+
trace_data[:address] = msg.to
|
78
|
+
end
|
79
|
+
|
80
|
+
if op[0,4] == 'PUSH'
|
81
|
+
trace_data[:pushvalue] = pushval
|
82
|
+
end
|
83
|
+
|
84
|
+
log_vm_op.trace('vm', **trace_data)
|
85
|
+
|
86
|
+
steps += 1
|
87
|
+
_prevop = op
|
88
|
+
end
|
89
|
+
|
90
|
+
# Invalid operation
|
91
|
+
return vm_exception('INVALID OP', opcode: opcode) if op == :INVALID
|
92
|
+
|
93
|
+
# Valid operations
|
94
|
+
stk = s.stack
|
95
|
+
mem = s.memory
|
96
|
+
if opcode < 0x10 # Stop & Arithmetic Operations
|
97
|
+
case op
|
98
|
+
when :STOP
|
99
|
+
return peaceful_exit('STOP', s.gas, [])
|
100
|
+
when :ADD
|
101
|
+
r = (stk.pop + stk.pop) & UINT_MAX
|
102
|
+
stk.push r
|
103
|
+
when :SUB
|
104
|
+
r = (stk.pop - stk.pop) & UINT_MAX
|
105
|
+
stk.push r
|
106
|
+
when :MUL
|
107
|
+
r = (stk.pop * stk.pop) & UINT_MAX
|
108
|
+
stk.push r
|
109
|
+
when :DIV
|
110
|
+
s0, s1 = stk.pop, stk.pop
|
111
|
+
stk.push(s1 == 0 ? 0 : s0 / s1)
|
112
|
+
when :MOD
|
113
|
+
s0, s1 = stk.pop, stk.pop
|
114
|
+
stk.push(s1 == 0 ? 0 : s0 % s1)
|
115
|
+
when :SDIV
|
116
|
+
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
|
117
|
+
r = s1 == 0 ? 0 : ((s0.abs / s1.abs * (s0*s1 < 0 ? -1 : 1)) & UINT_MAX)
|
118
|
+
stk.push r
|
119
|
+
when :SMOD
|
120
|
+
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
|
121
|
+
r = s1 == 0 ? 0 : ((s0.abs % s1.abs * (s0 < 0 ? -1 : 1)) & UINT_MAX)
|
122
|
+
stk.push r
|
123
|
+
when :ADDMOD
|
124
|
+
s0, s1, s2 = stk.pop, stk.pop, stk.pop
|
125
|
+
r = s2 == 0 ? 0 : (s0+s1) % s2
|
126
|
+
stk.push r
|
127
|
+
when :MULMOD
|
128
|
+
s0, s1, s2 = stk.pop, stk.pop, stk.pop
|
129
|
+
r = s2 == 0 ? 0 : Utils.mod_mul(s0, s1, s2)
|
130
|
+
stk.push r
|
131
|
+
when :EXP
|
132
|
+
base, exponent = stk.pop, stk.pop
|
133
|
+
|
134
|
+
# fee for exponent is dependent on its bytes
|
135
|
+
# calc n bytes to represent exponent
|
136
|
+
nbytes = Utils.encode_int(exponent).size
|
137
|
+
expfee = nbytes * Opcodes::GEXPONENTBYTE
|
138
|
+
if s.gas < expfee
|
139
|
+
s.gas = 0
|
140
|
+
return vm_exception('OOG EXPONENT')
|
141
|
+
end
|
142
|
+
|
143
|
+
s.gas -= expfee
|
144
|
+
stk.push Utils.mod_exp(base, exponent, TT256)
|
145
|
+
when :SIGNEXTEND # extend sign from bytes at s0 to left
|
146
|
+
s0, s1 = stk.pop, stk.pop
|
147
|
+
if s0 < 32
|
148
|
+
testbit = s0*8 + 7
|
149
|
+
mask = 1 << testbit
|
150
|
+
if s1 & mask == 0 # extend 0s
|
151
|
+
stk.push(s1 & (mask - 1))
|
152
|
+
else # extend 1s
|
153
|
+
stk.push(s1 | (TT256 - mask))
|
154
|
+
end
|
155
|
+
else
|
156
|
+
stk.push s1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
elsif opcode < 0x20 # Comparison & Bitwise Logic Operations
|
160
|
+
case op
|
161
|
+
when :LT
|
162
|
+
s0, s1 = stk.pop, stk.pop
|
163
|
+
stk.push(s0 < s1 ? 1 : 0)
|
164
|
+
when :GT
|
165
|
+
s0, s1 = stk.pop, stk.pop
|
166
|
+
stk.push(s0 > s1 ? 1 : 0)
|
167
|
+
when :SLT
|
168
|
+
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
|
169
|
+
stk.push(s0 < s1 ? 1 : 0)
|
170
|
+
when :SGT
|
171
|
+
s0, s1 = Utils.to_signed(stk.pop), Utils.to_signed(stk.pop)
|
172
|
+
stk.push(s0 > s1 ? 1 : 0)
|
173
|
+
when :EQ
|
174
|
+
s0, s1 = stk.pop, stk.pop
|
175
|
+
stk.push(s0 == s1 ? 1 : 0)
|
176
|
+
when :ISZERO
|
177
|
+
s0 = stk.pop
|
178
|
+
stk.push(s0 == 0 ? 1 : 0)
|
179
|
+
when :AND
|
180
|
+
s0, s1 = stk.pop, stk.pop
|
181
|
+
stk.push(s0 & s1)
|
182
|
+
when :OR
|
183
|
+
s0, s1 = stk.pop, stk.pop
|
184
|
+
stk.push(s0 | s1)
|
185
|
+
when :XOR
|
186
|
+
s0, s1 = stk.pop, stk.pop
|
187
|
+
stk.push(s0 ^ s1)
|
188
|
+
when :NOT
|
189
|
+
s0 = stk.pop
|
190
|
+
stk.push(UINT_MAX - s0)
|
191
|
+
when :BYTE
|
192
|
+
s0, s1 = stk.pop, stk.pop
|
193
|
+
if s0 < 32
|
194
|
+
stk.push((s1 / 256**(31-s0)) % 256)
|
195
|
+
else
|
196
|
+
stk.push(0)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
elsif opcode < 0x40 # SHA3 & Environmental Information
|
200
|
+
case op
|
201
|
+
when :SHA3
|
202
|
+
s0, s1 = stk.pop, stk.pop
|
203
|
+
|
204
|
+
s.gas -= Opcodes::GSHA3WORD * (Utils.ceil32(s1) / 32)
|
205
|
+
return vm_exception('OOG PAYING FOR SHA3') if s.gas < 0
|
206
|
+
|
207
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
|
208
|
+
|
209
|
+
data = Utils.int_array_to_bytes mem.safe_slice(s0,s1)
|
210
|
+
stk.push Utils.big_endian_to_int(Utils.keccak256(data))
|
211
|
+
when :ADDRESS
|
212
|
+
stk.push Utils.coerce_to_int(msg.to)
|
213
|
+
when :BALANCE
|
214
|
+
s0 = stk.pop
|
215
|
+
addr = Utils.coerce_addr_to_hex(s0 % 2**160)
|
216
|
+
stk.push ext.get_balance(addr)
|
217
|
+
when :ORIGIN
|
218
|
+
stk.push Utils.coerce_to_int(ext.tx_origin)
|
219
|
+
when :CALLER
|
220
|
+
stk.push Utils.coerce_to_int(msg.sender)
|
221
|
+
when :CALLVALUE
|
222
|
+
stk.push msg.value
|
223
|
+
when :CALLDATALOAD
|
224
|
+
stk.push msg.data.extract32(stk.pop)
|
225
|
+
when :CALLDATASIZE
|
226
|
+
stk.push msg.data.size
|
227
|
+
when :CALLDATACOPY
|
228
|
+
mstart, dstart, size = stk.pop, stk.pop, stk.pop
|
229
|
+
|
230
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
|
231
|
+
return vm_exception('OOG COPY DATA') unless data_copy(s, size)
|
232
|
+
|
233
|
+
msg.data.extract_copy(mem, mstart, dstart, size)
|
234
|
+
when :CODESIZE
|
235
|
+
stk.push processed_code.size
|
236
|
+
when :CODECOPY
|
237
|
+
mstart, cstart, size = stk.pop, stk.pop, stk.pop
|
238
|
+
|
239
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
|
240
|
+
return vm_exception('OOG COPY CODE') unless data_copy(s, size)
|
241
|
+
|
242
|
+
size.times do |i|
|
243
|
+
if cstart + i < processed_code.size
|
244
|
+
mem[mstart+i] = processed_code[cstart+i][4] # copy opcode
|
245
|
+
else
|
246
|
+
mem[mstart+i] = 0
|
247
|
+
end
|
248
|
+
end
|
249
|
+
when :GASPRICE
|
250
|
+
stk.push ext.tx_gasprice
|
251
|
+
when :EXTCODESIZE
|
252
|
+
addr = stk.pop
|
253
|
+
addr = Utils.coerce_addr_to_hex(addr % 2**160)
|
254
|
+
stk.push (ext.get_code(addr) || Constant::BYTE_EMPTY).size
|
255
|
+
when :EXTCODECOPY
|
256
|
+
addr, mstart, cstart, size = stk.pop, stk.pop, stk.pop, stk.pop
|
257
|
+
addr = Utils.coerce_addr_to_hex(addr % 2**160)
|
258
|
+
extcode = ext.get_code(addr) || Constant::BYTE_EMPTY
|
259
|
+
raise ValueError, "extcode must be string" unless extcode.is_a?(String)
|
260
|
+
|
261
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, size)
|
262
|
+
return vm_exception('OOG COPY CODE') unless data_copy(s, size)
|
263
|
+
|
264
|
+
size.times do |i|
|
265
|
+
if cstart + i < extcode.size
|
266
|
+
mem[mstart+i] = extcode[cstart+i].ord
|
267
|
+
else
|
268
|
+
mem[mstart+i] = 0
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
elsif opcode < 0x50 # Block Information
|
273
|
+
case op
|
274
|
+
when :BLOCKHASH
|
275
|
+
s0 = stk.pop
|
276
|
+
stk.push Utils.big_endian_to_int(ext.block_hash(s0))
|
277
|
+
when :COINBASE
|
278
|
+
stk.push Utils.big_endian_to_int(ext.block_coinbase)
|
279
|
+
when :TIMESTAMP
|
280
|
+
stk.push ext.block_timestamp
|
281
|
+
when :NUMBER
|
282
|
+
stk.push ext.block_number
|
283
|
+
when :DIFFICULTY
|
284
|
+
stk.push ext.block_difficulty
|
285
|
+
when :GASLIMIT
|
286
|
+
stk.push ext.block_gas_limit
|
287
|
+
end
|
288
|
+
elsif opcode < 0x60 # Stack, Memory, Storage and Flow Operations
|
289
|
+
case op
|
290
|
+
when :POP
|
291
|
+
stk.pop
|
292
|
+
when :MLOAD
|
293
|
+
s0 = stk.pop
|
294
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)
|
295
|
+
|
296
|
+
data = Utils.int_array_to_bytes mem.safe_slice(s0, 32)
|
297
|
+
stk.push Utils.big_endian_to_int(data)
|
298
|
+
when :MSTORE
|
299
|
+
s0, s1 = stk.pop, stk.pop
|
300
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 32)
|
301
|
+
|
302
|
+
32.times.to_a.reverse.each do |i|
|
303
|
+
mem[s0+i] = s1 % 256
|
304
|
+
s1 /= 256
|
305
|
+
end
|
306
|
+
when :MSTORE8
|
307
|
+
s0, s1 = stk.pop, stk.pop
|
308
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, 1)
|
309
|
+
mem[s0] = s1 % 256
|
310
|
+
when :SLOAD
|
311
|
+
s0 = stk.pop
|
312
|
+
stk.push ext.get_storage_data(msg.to, s0)
|
313
|
+
when :SSTORE
|
314
|
+
s0, s1 = stk.pop, stk.pop
|
315
|
+
|
316
|
+
if ext.get_storage_data(msg.to, s0) != 0
|
317
|
+
gascost = s1 == 0 ? Opcodes::GSTORAGEKILL : Opcodes::GSTORAGEMOD
|
318
|
+
refund = s1 == 0 ? Opcodes::GSTORAGEREFUND : 0
|
319
|
+
else
|
320
|
+
gascost = s1 == 0 ? Opcodes::GSTORAGEMOD : Opcodes::GSTORAGEADD
|
321
|
+
refund = 0
|
322
|
+
end
|
323
|
+
|
324
|
+
return vm_exception('OUT OF GAS') if s.gas < gascost
|
325
|
+
|
326
|
+
s.gas -= gascost
|
327
|
+
ext.add_refund refund
|
328
|
+
ext.set_storage_data msg.to, s0, s1
|
329
|
+
when :JUMP
|
330
|
+
s0 = stk.pop
|
331
|
+
s.pc = s0
|
332
|
+
|
333
|
+
op_new = s.pc < processed_code.size ? processed_code[s.pc][0] : :STOP
|
334
|
+
return vm_exception('BAD JUMPDEST') if op_new != :JUMPDEST
|
335
|
+
when :JUMPI
|
336
|
+
s0, s1 = stk.pop, stk.pop
|
337
|
+
if s1 != 0
|
338
|
+
s.pc = s0
|
339
|
+
op_new = s.pc < processed_code.size ? processed_code[s.pc][0] : :STOP
|
340
|
+
return vm_exception('BAD JUMPDEST') if op_new != :JUMPDEST
|
341
|
+
end
|
342
|
+
when :PC
|
343
|
+
stk.push(s.pc - 1)
|
344
|
+
when :MSIZE
|
345
|
+
stk.push mem.size
|
346
|
+
when :GAS
|
347
|
+
stk.push s.gas # AFTER subtracting cost 1
|
348
|
+
end
|
349
|
+
elsif op[0,Opcodes::PREFIX_PUSH.size] == Opcodes::PREFIX_PUSH
|
350
|
+
pushnum = op[Opcodes::PREFIX_PUSH.size..-1].to_i
|
351
|
+
s.pc += pushnum
|
352
|
+
stk.push pushval
|
353
|
+
elsif op[0,Opcodes::PREFIX_DUP.size] == Opcodes::PREFIX_DUP
|
354
|
+
depth = op[Opcodes::PREFIX_DUP.size..-1].to_i
|
355
|
+
stk.push stk[-depth]
|
356
|
+
elsif op[0,Opcodes::PREFIX_SWAP.size] == Opcodes::PREFIX_SWAP
|
357
|
+
depth = op[Opcodes::PREFIX_SWAP.size..-1].to_i
|
358
|
+
temp = stk[-depth - 1]
|
359
|
+
stk[-depth - 1] = stk[-1]
|
360
|
+
stk[-1] = temp
|
361
|
+
elsif op[0,Opcodes::PREFIX_LOG.size] == Opcodes::PREFIX_LOG
|
362
|
+
# 0xa0 ... 0xa4, 32/64/96/128/160 + data.size gas
|
363
|
+
#
|
364
|
+
# a. Opcodes LOG0...LOG4 are added, takes 2-6 stake arguments:
|
365
|
+
# MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4)
|
366
|
+
#
|
367
|
+
# b. Logs are kept track of during tx execution exactly the same way
|
368
|
+
# as suicides (except as an ordered list, not a set).
|
369
|
+
#
|
370
|
+
# Each log is in the form [address, [topic1, ... ], data] where:
|
371
|
+
# * address is what the ADDRESS opcode would output
|
372
|
+
# * data is mem[MEMSTART, MEMSZ]
|
373
|
+
# * topics are as provided by the opcode
|
374
|
+
#
|
375
|
+
# c. The ordered list of logs in the transation are expreseed as
|
376
|
+
# [log0, log1, ..., logN].
|
377
|
+
#
|
378
|
+
depth = op[Opcodes::PREFIX_LOG.size..-1].to_i
|
379
|
+
mstart, msz = stk.pop, stk.pop
|
380
|
+
topics = depth.times.map {|i| stk.pop }
|
381
|
+
|
382
|
+
s.gas -= msz * Opcodes::GLOGBYTE
|
383
|
+
|
384
|
+
return vm_exception("OOG EXTENDING MEMORY") unless mem_extend(mem, s, mstart, msz)
|
385
|
+
|
386
|
+
data = mem.safe_slice(mstart, msz)
|
387
|
+
ext.log(msg.to, topics, Utils.int_array_to_bytes(data))
|
388
|
+
log_log.trace('LOG', to: msg.to, topics: topics, data: data)
|
389
|
+
elsif op == :CREATE
|
390
|
+
value, mstart, msz = stk.pop, stk.pop, stk.pop
|
391
|
+
|
392
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, mstart, msz)
|
393
|
+
|
394
|
+
if ext.get_balance(msg.to) >= value && msg.depth < 1024
|
395
|
+
cd = CallData.new mem, mstart, msz
|
396
|
+
create_msg = Message.new(msg.to, Constant::BYTE_EMPTY, value, s.gas, cd, depth: msg.depth+1)
|
397
|
+
|
398
|
+
o, gas, addr = ext.create create_msg
|
399
|
+
if o.true?
|
400
|
+
stk.push Utils.coerce_to_int(addr)
|
401
|
+
s.gas = gas
|
402
|
+
else
|
403
|
+
stk.push 0
|
404
|
+
s.gas = 0
|
405
|
+
end
|
406
|
+
else
|
407
|
+
stk.push(0)
|
408
|
+
end
|
409
|
+
elsif op == :CALL
|
410
|
+
gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
|
411
|
+
stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
|
412
|
+
|
413
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
|
414
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
|
415
|
+
|
416
|
+
to = Utils.zpad_int(to)[12..-1] # last 20 bytes
|
417
|
+
extra_gas = (ext.account_exists(to) ? 0 : 1) * Opcodes::GCALLNEWACCOUNT +
|
418
|
+
(value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
|
419
|
+
submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
|
420
|
+
total_gas = gas + extra_gas
|
421
|
+
|
422
|
+
return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
|
423
|
+
|
424
|
+
if ext.get_balance(msg.to) >= value && msg.depth < 1024
|
425
|
+
s.gas -= total_gas
|
426
|
+
|
427
|
+
cd = CallData.new mem, memin_start, memin_sz
|
428
|
+
call_msg = Message.new(msg.to, to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
|
429
|
+
|
430
|
+
result, gas, data = ext.apply_msg call_msg
|
431
|
+
if result == 0
|
432
|
+
stk.push 0
|
433
|
+
else
|
434
|
+
stk.push 1
|
435
|
+
s.gas += gas
|
436
|
+
[data.size, memout_sz].min.times do |i|
|
437
|
+
mem[memout_start+i] = data[i]
|
438
|
+
end
|
439
|
+
end
|
440
|
+
else
|
441
|
+
s.gas -= (total_gas - submsg_gas)
|
442
|
+
stk.push(0)
|
443
|
+
end
|
444
|
+
elsif op == :CALLCODE || op == :DELEGATECALL
|
445
|
+
if op == :CALLCODE
|
446
|
+
gas, to, value, memin_start, memin_sz, memout_start, memout_sz = \
|
447
|
+
stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
|
448
|
+
else
|
449
|
+
gas, to, memin_start, memin_sz, memout_start, memout_sz = \
|
450
|
+
stk.pop, stk.pop, stk.pop, stk.pop, stk.pop, stk.pop
|
451
|
+
value = 0
|
452
|
+
end
|
453
|
+
|
454
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memin_start, memin_sz)
|
455
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, memout_start, memout_sz)
|
456
|
+
|
457
|
+
extra_gas = (value > 0 ? 1 : 0) * Opcodes::GCALLVALUETRANSFER
|
458
|
+
submsg_gas = gas + Opcodes::GSTIPEND * (value > 0 ? 1 : 0)
|
459
|
+
total_gas = gas + extra_gas
|
460
|
+
|
461
|
+
return vm_exception('OUT OF GAS', needed: total_gas) if s.gas < total_gas
|
462
|
+
|
463
|
+
if ext.get_balance(msg.to) >= value && msg.depth < 1024
|
464
|
+
s.gas -= total_gas
|
465
|
+
|
466
|
+
to = Utils.zpad_int(to)[12..-1] # last 20 bytes
|
467
|
+
cd = CallData.new mem, memin_start, memin_sz
|
468
|
+
|
469
|
+
if ext.post_homestead_hardfork && op == :DELEGATECALL
|
470
|
+
call_msg = Message.new(msg.sender, msg.to, msg.value, submsg_gas, cd, depth: msg.depth+1, code_address: to, transfers_value: false)
|
471
|
+
elsif op == :DELEGATECALL
|
472
|
+
return vm_exception('OPCODE INACTIVE')
|
473
|
+
else
|
474
|
+
call_msg = Message.new(msg.to, msg.to, value, submsg_gas, cd, depth: msg.depth+1, code_address: to)
|
475
|
+
end
|
476
|
+
|
477
|
+
result, gas, data = ext.apply_msg call_msg
|
478
|
+
if result == 0
|
479
|
+
stk.push 0
|
480
|
+
else
|
481
|
+
stk.push 1
|
482
|
+
s.gas += gas
|
483
|
+
[data.size, memout_sz].min.times do |i|
|
484
|
+
mem[memout_start+i] = data[i]
|
485
|
+
end
|
486
|
+
end
|
487
|
+
else
|
488
|
+
s.gas -= (total_gas - submsg_gas)
|
489
|
+
stk.push(0)
|
490
|
+
end
|
491
|
+
elsif op == :RETURN
|
492
|
+
s0, s1 = stk.pop, stk.pop
|
493
|
+
return vm_exception('OOG EXTENDING MEMORY') unless mem_extend(mem, s, s0, s1)
|
494
|
+
return peaceful_exit('RETURN', s.gas, mem.safe_slice(s0, s1))
|
495
|
+
elsif op == :SUICIDE
|
496
|
+
s0 = stk.pop
|
497
|
+
to = Utils.zpad_int(s0)[12..-1] # last 20 bytes
|
498
|
+
|
499
|
+
xfer = ext.get_balance(msg.to)
|
500
|
+
ext.set_balance(to, ext.get_balance(to)+xfer)
|
501
|
+
ext.set_balance(msg.to, 0)
|
502
|
+
ext.add_suicide(msg.to)
|
503
|
+
|
504
|
+
return 1, s.gas, []
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# Preprocesses code, and determines which locations are in the middle of
|
510
|
+
# pushdata and thus invalid
|
511
|
+
def preprocess_code(code)
|
512
|
+
code = Utils.bytes_to_int_array code
|
513
|
+
ops = []
|
514
|
+
|
515
|
+
i = 0
|
516
|
+
while i < code.size
|
517
|
+
o = Opcodes::TABLE.fetch(code[i], [:INVALID, 0, 0, 0]) + [code[i], 0]
|
518
|
+
ops.push o
|
519
|
+
|
520
|
+
if o[0][0,Opcodes::PREFIX_PUSH.size] == Opcodes::PREFIX_PUSH
|
521
|
+
n = o[0][Opcodes::PREFIX_PUSH.size..-1].to_i
|
522
|
+
n.times do |j|
|
523
|
+
i += 1
|
524
|
+
byte = i < code.size ? code[i] : 0
|
525
|
+
o[-1] = (o[-1] << 8) + byte
|
526
|
+
|
527
|
+
# polyfill, these INVALID ops will be skipped in execution
|
528
|
+
ops.push [:INVALID, 0, 0, 0, byte, 0] if i < code.size
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
i += 1
|
533
|
+
end
|
534
|
+
|
535
|
+
ops
|
536
|
+
end
|
537
|
+
|
538
|
+
private
|
539
|
+
|
540
|
+
def log_vm_exit
|
541
|
+
@log_vm_exit ||= Logger.new 'eth.vm.exit'
|
542
|
+
end
|
543
|
+
|
544
|
+
def log_vm_op
|
545
|
+
@log_vm_op ||= Logger.new 'eth.vm.op'
|
546
|
+
end
|
547
|
+
|
548
|
+
def log_log
|
549
|
+
@log_log ||= Logger.new 'eth.vm.log'
|
550
|
+
end
|
551
|
+
|
552
|
+
def vm_exception(error, **kwargs)
|
553
|
+
log_vm_exit.trace('EXCEPTION', cause: error, **kwargs)
|
554
|
+
return 0, 0, []
|
555
|
+
end
|
556
|
+
|
557
|
+
def peaceful_exit(cause, gas, data, **kwargs)
|
558
|
+
log_vm_exit.trace('EXIT', cause: cause, **kwargs)
|
559
|
+
return 1, gas, data
|
560
|
+
end
|
561
|
+
|
562
|
+
def mem_extend(mem, s, start, sz)
|
563
|
+
if sz > 0
|
564
|
+
oldsize = mem.size / 32
|
565
|
+
old_totalfee = mem_fee oldsize
|
566
|
+
|
567
|
+
newsize = Utils.ceil32(start + sz) / 32
|
568
|
+
new_totalfee = mem_fee newsize
|
569
|
+
|
570
|
+
if old_totalfee < new_totalfee
|
571
|
+
memfee = new_totalfee - old_totalfee
|
572
|
+
|
573
|
+
if s.gas < memfee
|
574
|
+
s.gas = 0
|
575
|
+
return false
|
576
|
+
end
|
577
|
+
s.gas -= memfee
|
578
|
+
|
579
|
+
m_extend = (newsize - oldsize) * 32
|
580
|
+
mem.concat([0]*m_extend)
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
true
|
585
|
+
end
|
586
|
+
|
587
|
+
def data_copy(s, sz)
|
588
|
+
if sz > 0
|
589
|
+
copyfee = Opcodes::GCOPY * Utils.ceil32(sz) / 32
|
590
|
+
|
591
|
+
if s.gas < copyfee
|
592
|
+
s.gas = 0
|
593
|
+
return false
|
594
|
+
end
|
595
|
+
s.gas -= copyfee
|
596
|
+
end
|
597
|
+
|
598
|
+
true
|
599
|
+
end
|
600
|
+
|
601
|
+
def mem_fee(sz)
|
602
|
+
sz * Opcodes::GMEMORY + sz**2 / Opcodes::GQUADRATICMEMDENOM
|
603
|
+
end
|
604
|
+
|
605
|
+
end
|
606
|
+
end
|