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,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
@@ -0,0 +1,5 @@
1
+ # -*- encoding : ascii-8bit -*-
2
+
3
+ module Ethereum
4
+ VERSION = '0.9.0'
5
+ end
@@ -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