ruby-ethereum 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +40 -0
  4. data/lib/ethereum.rb +53 -0
  5. data/lib/ethereum/abi.rb +333 -0
  6. data/lib/ethereum/abi/contract_translator.rb +174 -0
  7. data/lib/ethereum/abi/type.rb +117 -0
  8. data/lib/ethereum/account.rb +72 -0
  9. data/lib/ethereum/address.rb +60 -0
  10. data/lib/ethereum/base_convert.rb +53 -0
  11. data/lib/ethereum/block.rb +1311 -0
  12. data/lib/ethereum/block_header.rb +211 -0
  13. data/lib/ethereum/bloom.rb +83 -0
  14. data/lib/ethereum/cached_block.rb +36 -0
  15. data/lib/ethereum/chain.rb +400 -0
  16. data/lib/ethereum/constant.rb +26 -0
  17. data/lib/ethereum/core_ext/array/safe_slice.rb +18 -0
  18. data/lib/ethereum/core_ext/module/lru_cache.rb +20 -0
  19. data/lib/ethereum/core_ext/numeric/denominations.rb +45 -0
  20. data/lib/ethereum/core_ext/object/truth.rb +47 -0
  21. data/lib/ethereum/db.rb +6 -0
  22. data/lib/ethereum/db/base_db.rb +9 -0
  23. data/lib/ethereum/db/ephem_db.rb +63 -0
  24. data/lib/ethereum/db/overlay_db.rb +72 -0
  25. data/lib/ethereum/db/refcount_db.rb +188 -0
  26. data/lib/ethereum/env.rb +64 -0
  27. data/lib/ethereum/ethash.rb +78 -0
  28. data/lib/ethereum/ethash_ruby.rb +38 -0
  29. data/lib/ethereum/ethash_ruby/cache.rb +47 -0
  30. data/lib/ethereum/ethash_ruby/hashimoto.rb +75 -0
  31. data/lib/ethereum/ethash_ruby/utils.rb +53 -0
  32. data/lib/ethereum/exceptions.rb +28 -0
  33. data/lib/ethereum/external_call.rb +173 -0
  34. data/lib/ethereum/fast_rlp.rb +81 -0
  35. data/lib/ethereum/fast_vm.rb +625 -0
  36. data/lib/ethereum/fast_vm/call_data.rb +44 -0
  37. data/lib/ethereum/fast_vm/message.rb +29 -0
  38. data/lib/ethereum/fast_vm/state.rb +25 -0
  39. data/lib/ethereum/ffi/openssl.rb +390 -0
  40. data/lib/ethereum/index.rb +97 -0
  41. data/lib/ethereum/log.rb +43 -0
  42. data/lib/ethereum/miner.rb +84 -0
  43. data/lib/ethereum/opcodes.rb +131 -0
  44. data/lib/ethereum/private_key.rb +92 -0
  45. data/lib/ethereum/pruning_trie.rb +28 -0
  46. data/lib/ethereum/public_key.rb +88 -0
  47. data/lib/ethereum/receipt.rb +53 -0
  48. data/lib/ethereum/secp256k1.rb +58 -0
  49. data/lib/ethereum/secure_trie.rb +49 -0
  50. data/lib/ethereum/sedes.rb +42 -0
  51. data/lib/ethereum/special_contract.rb +95 -0
  52. data/lib/ethereum/spv.rb +79 -0
  53. data/lib/ethereum/spv/proof.rb +31 -0
  54. data/lib/ethereum/spv/proof_constructor.rb +19 -0
  55. data/lib/ethereum/spv/proof_verifier.rb +24 -0
  56. data/lib/ethereum/tester.rb +14 -0
  57. data/lib/ethereum/tester/abi_contract.rb +65 -0
  58. data/lib/ethereum/tester/fixture.rb +31 -0
  59. data/lib/ethereum/tester/language.rb +30 -0
  60. data/lib/ethereum/tester/log_recorder.rb +13 -0
  61. data/lib/ethereum/tester/solidity_wrapper.rb +144 -0
  62. data/lib/ethereum/tester/state.rb +194 -0
  63. data/lib/ethereum/transaction.rb +196 -0
  64. data/lib/ethereum/transient_trie.rb +28 -0
  65. data/lib/ethereum/trie.rb +549 -0
  66. data/lib/ethereum/trie/nibble_key.rb +184 -0
  67. data/lib/ethereum/utils.rb +191 -0
  68. data/lib/ethereum/version.rb +5 -0
  69. data/lib/ethereum/vm.rb +606 -0
  70. data/lib/ethereum/vm/call_data.rb +40 -0
  71. data/lib/ethereum/vm/message.rb +30 -0
  72. data/lib/ethereum/vm/state.rb +25 -0
  73. metadata +284 -0
@@ -0,0 +1,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