ciri 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +0 -1
  4. data/Gemfile.lock +11 -7
  5. data/LICENSE.txt +201 -21
  6. data/README.md +78 -25
  7. data/Rakefile +45 -17
  8. data/bin/bundle +105 -0
  9. data/bin/htmldiff +29 -0
  10. data/bin/ldiff +29 -0
  11. data/bin/rake +29 -0
  12. data/bin/rspec +29 -0
  13. data/ciri-crypto/.gitignore +11 -0
  14. data/ciri-crypto/.rspec +3 -0
  15. data/ciri-crypto/.travis.yml +5 -0
  16. data/ciri-crypto/CODE_OF_CONDUCT.md +74 -0
  17. data/ciri-crypto/Gemfile +6 -0
  18. data/ciri-crypto/Gemfile.lock +43 -0
  19. data/ciri-crypto/LICENSE.txt +201 -0
  20. data/ciri-crypto/README.md +58 -0
  21. data/ciri-crypto/Rakefile +6 -0
  22. data/ciri-crypto/bin/console +14 -0
  23. data/ciri-crypto/bin/setup +8 -0
  24. data/ciri-crypto/ciri-crypto.gemspec +40 -0
  25. data/ciri-crypto/lib/ciri/crypto/errors.rb +29 -0
  26. data/ciri-crypto/lib/ciri/crypto/signature.rb +71 -0
  27. data/ciri-crypto/lib/ciri/crypto/version.rb +5 -0
  28. data/{lib → ciri-crypto/lib}/ciri/crypto.rb +16 -69
  29. data/ciri-crypto/spec/ciri/crypto_spec.rb +56 -0
  30. data/ciri-crypto/spec/spec_helper.rb +14 -0
  31. data/ciri-rlp/Gemfile.lock +5 -5
  32. data/ciri-rlp/LICENSE.txt +201 -21
  33. data/ciri-rlp/README.md +1 -1
  34. data/ciri-rlp/ciri-rlp.gemspec +3 -3
  35. data/ciri-rlp/lib/ciri/rlp/decode.rb +10 -16
  36. data/ciri-rlp/lib/ciri/rlp/encode.rb +10 -16
  37. data/ciri-rlp/lib/ciri/rlp/serializable.rb +10 -16
  38. data/ciri-rlp/lib/ciri/rlp.rb +10 -16
  39. data/ciri-rlp/spec/ciri/fixture_spec.rb +10 -16
  40. data/ciri-rlp/spec/ciri/rlp/serializable_spec.rb +10 -16
  41. data/ciri-utils/Gemfile.lock +4 -4
  42. data/ciri-utils/LICENSE.txt +201 -21
  43. data/ciri-utils/README.md +1 -1
  44. data/ciri-utils/ciri-utils.gemspec +2 -2
  45. data/ciri-utils/lib/ciri/utils/logger.rb +10 -16
  46. data/ciri-utils/lib/ciri/utils/number.rb +10 -16
  47. data/ciri-utils/lib/ciri/utils/version.rb +1 -1
  48. data/ciri-utils/lib/ciri/utils.rb +3 -3
  49. data/ciri.gemspec +4 -3
  50. data/docker/{Base → Dockerfile} +9 -3
  51. data/lib/ciri/actor.rb +10 -16
  52. data/lib/ciri/bloom_filter.rb +11 -17
  53. data/lib/ciri/chain/block.rb +10 -16
  54. data/lib/ciri/chain/header.rb +12 -18
  55. data/lib/ciri/chain/header_chain.rb +8 -22
  56. data/lib/ciri/chain/transaction.rb +38 -33
  57. data/lib/ciri/chain.rb +27 -26
  58. data/lib/ciri/core_ext.rb +61 -0
  59. data/lib/ciri/db/account_db.rb +25 -21
  60. data/lib/ciri/db/backend/memory.rb +10 -16
  61. data/lib/ciri/db/backend/rocks.rb +10 -16
  62. data/lib/ciri/db/backend/rocks_db.rb +10 -16
  63. data/lib/ciri/devp2p/peer.rb +10 -16
  64. data/lib/ciri/devp2p/protocol.rb +10 -16
  65. data/lib/ciri/devp2p/protocol_io.rb +10 -16
  66. data/lib/ciri/devp2p/rlpx/connection.rb +10 -16
  67. data/lib/ciri/devp2p/rlpx/encryption_handshake.rb +13 -19
  68. data/lib/ciri/devp2p/rlpx/error.rb +10 -16
  69. data/lib/ciri/devp2p/rlpx/frame_io.rb +10 -16
  70. data/lib/ciri/devp2p/rlpx/message.rb +10 -16
  71. data/lib/ciri/devp2p/rlpx/node.rb +10 -16
  72. data/lib/ciri/devp2p/rlpx/protocol_handshake.rb +10 -16
  73. data/lib/ciri/devp2p/rlpx/protocol_messages.rb +10 -16
  74. data/lib/ciri/devp2p/rlpx/secrets.rb +10 -16
  75. data/lib/ciri/devp2p/rlpx.rb +10 -16
  76. data/lib/ciri/devp2p/server.rb +10 -16
  77. data/lib/ciri/eth/peer.rb +10 -16
  78. data/lib/ciri/eth/protocol_manage.rb +10 -16
  79. data/lib/ciri/eth/protocol_messages.rb +10 -16
  80. data/lib/ciri/eth/synchronizer.rb +10 -16
  81. data/lib/ciri/eth.rb +10 -16
  82. data/lib/ciri/ethash.rb +10 -16
  83. data/lib/ciri/evm/block_info.rb +25 -17
  84. data/lib/ciri/evm/errors.rb +13 -16
  85. data/lib/ciri/evm/execution_context.rb +136 -0
  86. data/lib/ciri/evm/instruction.rb +17 -24
  87. data/lib/ciri/evm/log_entry.rb +12 -18
  88. data/lib/ciri/evm/machine_state.rb +28 -33
  89. data/lib/ciri/evm/op.rb +52 -89
  90. data/lib/ciri/evm/op_call.rb +114 -0
  91. data/lib/ciri/evm/precompile_contract.rb +102 -0
  92. data/lib/ciri/evm/state.rb +28 -21
  93. data/lib/ciri/evm/sub_state.rb +18 -24
  94. data/lib/ciri/evm/vm.rb +128 -190
  95. data/lib/ciri/evm.rb +77 -85
  96. data/lib/ciri/forks/base.rb +28 -16
  97. data/lib/ciri/forks/byzantium.rb +43 -0
  98. data/lib/ciri/forks/config.rb +36 -0
  99. data/lib/ciri/forks/frontier/cost.rb +42 -33
  100. data/lib/ciri/forks/frontier.rb +47 -18
  101. data/lib/ciri/forks/homestead/cost.rb +195 -0
  102. data/lib/ciri/forks/homestead.rb +46 -0
  103. data/lib/ciri/forks.rb +12 -22
  104. data/lib/ciri/key.rb +14 -3
  105. data/lib/ciri/pow.rb +11 -17
  106. data/lib/ciri/rlp/decode.rb +10 -16
  107. data/lib/ciri/rlp/encode.rb +10 -16
  108. data/lib/ciri/rlp/serializable.rb +10 -16
  109. data/lib/ciri/serialize.rb +14 -17
  110. data/lib/ciri/trie/nibbles.rb +10 -16
  111. data/lib/ciri/trie/nodes.rb +12 -17
  112. data/lib/ciri/trie.rb +12 -18
  113. data/lib/ciri/types/account.rb +15 -17
  114. data/lib/ciri/types/address.rb +11 -17
  115. data/lib/ciri/types/errors.rb +10 -16
  116. data/lib/ciri/types/receipt.rb +12 -18
  117. data/lib/ciri/types/uint.rb +79 -0
  118. data/lib/ciri/version.rb +1 -1
  119. data/lib/ciri.rb +10 -16
  120. metadata +54 -10
  121. data/lib/ciri/types/number.rb +0 -73
@@ -1,24 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
3
+ # Copyright 2018 Jiang Jinyang <https://justjjy.com>
4
4
  #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
11
8
  #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
14
10
  #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
22
16
 
23
17
 
24
18
  module Ciri
@@ -27,17 +21,17 @@ module Ciri
27
21
  # sub state contained changed accounts and log_series
28
22
  class SubState
29
23
 
30
- EMPTY = SubState.new.freeze
31
-
32
24
  attr_reader :suicide_accounts, :log_series, :touched_accounts, :refunds
33
25
 
34
26
  def initialize(suicide_accounts: [], log_series: [], touched_accounts: [], refunds: [])
35
- @suicide_accounts = Set.new(suicide_accounts)
27
+ @suicide_accounts = suicide_accounts
36
28
  @log_series = log_series
37
- @touched_accounts = Set.new(touched_accounts)
38
- @refunds = Set.new(refunds)
29
+ @touched_accounts = touched_accounts
30
+ @refunds = refunds
39
31
  end
40
32
 
33
+ EMPTY = SubState.new.freeze
34
+
41
35
  # support safety copy
42
36
  def initialize_copy(orig)
43
37
  super
@@ -48,15 +42,15 @@ module Ciri
48
42
  end
49
43
 
50
44
  def add_refund_account(account)
51
- @refunds.add account
45
+ @refunds << account
52
46
  end
53
47
 
54
48
  def add_touched_account(account)
55
- @touched_accounts.add account
49
+ @touched_accounts << account
56
50
  end
57
51
 
58
52
  def add_suicide_account(account)
59
- @suicide_accounts.add account
53
+ @suicide_accounts << account
60
54
  end
61
55
  end
62
56
 
data/lib/ciri/evm/vm.rb CHANGED
@@ -1,34 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
3
+ # Copyright 2018 Jiang Jinyang <https://justjjy.com>
4
4
  #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
11
8
  #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
9
+ # http://www.apache.org/licenses/LICENSE-2.0
14
10
  #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
22
16
 
23
17
 
24
18
  require 'ciri/utils/logger'
19
+ require 'ciri/core_ext'
25
20
  require_relative 'errors'
21
+ require_relative 'execution_context'
26
22
  require_relative 'machine_state'
27
23
  require_relative 'instruction'
28
24
  require_relative 'sub_state'
29
25
  require_relative 'block_info'
30
26
  require_relative 'log_entry'
31
27
 
28
+ using Ciri::CoreExt
32
29
  module Ciri
33
30
  class EVM
34
31
 
@@ -42,57 +39,30 @@ module Ciri
42
39
  # other logic of EVM (include transaction logic) in EVM module.
43
40
  class VM
44
41
 
45
- class << self
46
- # this method provide a simpler interface to create VM and execute code
47
- # VM.spawn(...) == VM.new(...)
48
- # @return VM
49
- def spawn(state:, gas_limit:, header: nil, block_info: nil, instruction: EVM::Instruction.new, fork_config:)
50
- ms = MachineState.new(remain_gas: gas_limit, pc: 0, stack: [], memory: "\x00".b * 256, memory_item: 0)
51
-
52
- block_info = block_info || header && BlockInfo.new(
53
- coinbase: header.beneficiary,
54
- difficulty: header.difficulty,
55
- gas_limit: header.gas_limit,
56
- number: header.number,
57
- timestamp: header.timestamp
58
- )
59
-
60
- vm = VM.new(
61
- state: state,
62
- machine_state: ms,
63
- block_info: block_info,
64
- instruction: instruction,
65
- fork_config: fork_config
66
- )
67
- yield vm if block_given?
68
- vm
69
- end
70
- end
71
-
72
42
  extend Forwardable
73
43
 
74
44
  # helper methods
75
45
  include Utils::Logger
76
46
 
77
- def_delegators :@machine_state, :stack, :pc, :pop, :push, :pop_list, :get_stack, :memory_item, :memory_item=,
78
- :memory_store, :memory_fetch, :extend_memory, :remain_gas, :consume_gas
79
- def_delegators :@instruction, :get_op, :get_code, :next_valid_instruction_pos, :get_data, :data, :sender
80
- def_delegators :@sub_state, :add_refund_account, :add_touched_account, :add_suicide_account
81
- def_delegators :@state, :find_account, :account_dead?, :store, :fetch, :set_account_code, :get_account_code
47
+ def_delegators :machine_state, :stack, :pop, :push, :pop_list, :get_stack, :memory_item, :memory_item=,
48
+ :memory_store, :memory_fetch, :extend_memory
49
+ def_delegators :state, :find_account, :account_dead?, :store, :fetch,
50
+ :set_account_code, :get_account_code, :account_exist?
82
51
 
83
- attr_reader :state, :machine_state, :instruction, :sub_state, :block_info, :fork_config
84
- attr_accessor :output, :exception
52
+ # delegate methods to current execution_context
53
+ def_delegators :execution_context, :instruction, :sub_state, :machine_state, :block_info, :fork_schema,
54
+ :pc, :output, :exception, :set_output, :set_exception, :set_pc, :status, :depth,
55
+ :gas_limit, :refund_gas, :reset_refund_gas, :consume_gas, :remain_gas, :jump_to, :jump_pc
56
+ def_delegators :instruction, :get_op, :get_code, :next_valid_instruction_pos, :get_data, :data, :sender, :destinations
57
+ def_delegators :sub_state, :add_refund_account, :add_touched_account, :add_suicide_account
85
58
 
86
- def initialize(state:, machine_state:, sub_state: nil, instruction:, block_info:,
87
- fork_config:, burn_gas_on_exception: true)
59
+ attr_reader :state, :execution_context, :burn_gas_on_exception, :max_depth, :stack_size
60
+
61
+ def initialize(state:, burn_gas_on_exception: true, max_depth: 1024, stack_size: 1024)
88
62
  @state = state
89
- @machine_state = machine_state
90
- @instruction = instruction
91
- @sub_state = sub_state || SubState.new
92
- @output = nil
93
- @block_info = block_info
94
- @fork_config = fork_config
95
63
  @burn_gas_on_exception = burn_gas_on_exception
64
+ @max_depth = max_depth
65
+ @stack_size = stack_size
96
66
  end
97
67
 
98
68
  # run vm
@@ -103,54 +73,52 @@ module Ciri
103
73
 
104
74
  # low_level create_contract interface
105
75
  # CREATE_CONTRACT op is based on this method
106
- def create_contract(value:, init:)
107
- caller_address = instruction.address
76
+ def create_contract(context: self.execution_context)
77
+ caller_address = context.instruction.sender
78
+ value = context.instruction.value
108
79
  account = find_account(caller_address)
109
80
 
110
81
  # return contract address 0 represent execution failed
111
- return 0 unless account.balance >= value || instruction.execute_depth > 1024
112
-
113
- state.increment_nonce(caller_address)
82
+ return 0 unless account.balance >= value || depth > max_depth
114
83
  snapshot = state.snapshot
115
84
 
116
85
  # generate contract_address
117
- material = RLP.encode_simple([caller_address.to_s, account.nonce])
118
- contract_address = Utils.sha3(material)[-20..-1]
86
+ material = RLP.encode_simple([caller_address.to_s, account.nonce - 1])
87
+ contract_address = Utils.keccak(material)[-20..-1]
88
+
89
+ transact(sender: caller_address, value: value, to: contract_address)
119
90
 
120
91
  # initialize contract account
121
92
  contract_account = find_account(contract_address)
122
- # contract_account.nonce = 1
123
-
124
- # execute initialize code
125
- create_contract_instruction = instruction.dup
126
- create_contract_instruction.bytes_code = init
127
- create_contract_instruction.execute_depth += 1
128
- create_contract_instruction.address = contract_address
129
-
130
- # TODO refactoring: Maybe should use call_message to execute data
131
- call_instruction(create_contract_instruction) do
132
- execute
93
+ context.instruction.address = contract_address
94
+ with_context(context) do
95
+ if contract_account.has_code? || contract_account.nonce > 0
96
+ err = ContractCollisionError.new("address #{contract_address.to_hex} collision")
97
+ debug(err.message)
98
+ set_exception(err)
99
+ else
100
+ execute
101
+ end
133
102
 
134
- deposit_code_gas = fork_config.calculate_deposit_code_gas(output)
103
+ deposit_code_gas = fork_schema.calculate_deposit_code_gas(output)
135
104
 
136
105
  if deposit_code_gas > remain_gas
137
106
  # deposit_code_gas not enough
138
107
  contract_address = 0
139
108
  elsif exception
140
- state.touch_account(contract_address)
109
+ # state.touch_account(contract_address)
141
110
  contract_address = 0
111
+ if burn_gas_on_exception
112
+ debug("exception: #{exception}, burn gas #{remain_gas} to zero... op code: 0x#{get_op(pc).to_s(16)}")
113
+ consume_gas remain_gas
114
+ end
115
+ execution_context.revert
142
116
  state.revert(snapshot)
143
117
  else
144
118
  # set contract code
145
119
  set_account_code(contract_address, output)
146
120
  # minus deposit_code_fee
147
- machine_state.consume_gas deposit_code_gas
148
- # transact value
149
- account.balance -= value
150
- contract_account.balance += value
151
-
152
- state.set_balance(contract_address, contract_account.balance)
153
- state.set_balance(caller_address, account.balance)
121
+ consume_gas deposit_code_gas
154
122
  state.commit(snapshot)
155
123
  end
156
124
  [contract_address, exception]
@@ -159,30 +127,34 @@ module Ciri
159
127
 
160
128
  # low level call message interface
161
129
  # CALL, CALLCODE, DELEGATECALL ops is base on this method
162
- def call_message(sender:, value:, receipt:, data:, code_address: receipt)
130
+ def call_message(context: self.execution_context, code_address: context.instruction.address)
131
+ address = context.instruction.address
132
+ value = context.instruction.value
133
+ sender = context.instruction.sender
163
134
  # return status code 0 represent execution failed
164
- return [0, ''.b] unless value <= find_account(sender).balance && instruction.execute_depth <= 1024
165
-
166
- state.increment_nonce(sender)
135
+ return [0, ''.b] unless value <= find_account(sender).balance && depth <= max_depth
167
136
 
168
137
  snapshot = state.snapshot
169
-
170
- transact(sender: sender, value: value, to: receipt)
171
-
172
- message_call_instruction = instruction.dup
173
- message_call_instruction.address = receipt
174
- message_call_instruction.sender = sender
175
- message_call_instruction.value = value
176
-
177
- message_call_instruction.execute_depth += 1
178
-
179
- message_call_instruction.data = data
180
- message_call_instruction.bytes_code = get_account_code(code_address)
181
-
182
- call_instruction(message_call_instruction) do
183
- execute
138
+ transact(sender: sender, value: value, to: address)
139
+ # enter new execution context
140
+ with_context(context) do
141
+ begin
142
+ if (precompile_contract = fork_schema.find_precompile_contract(code_address))
143
+ precompile_contract.call(self)
144
+ else
145
+ execute
146
+ end
147
+ rescue GasNotEnoughError => e
148
+ set_exception(e)
149
+ end
184
150
 
185
151
  if exception
152
+ if burn_gas_on_exception
153
+ debug("exception: #{exception}, burn gas #{remain_gas} to zero... op code: 0x#{get_op(pc).to_s(16)}")
154
+ consume_gas remain_gas
155
+ end
156
+ execution_context.revert
157
+
186
158
  state.revert(snapshot)
187
159
  else
188
160
  state.commit(snapshot)
@@ -192,16 +164,6 @@ module Ciri
192
164
  end
193
165
  end
194
166
 
195
- def status
196
- exception.nil? ? 0 : 1
197
- end
198
-
199
- # jump to pc
200
- # only valid if current op code is allowed to modify pc
201
- def jump_to(pc)
202
- @jump_to = pc
203
- end
204
-
205
167
  def add_log_entry(topics, log_data)
206
168
  sub_state.log_series << LogEntry.new(address: instruction.address, topics: topics, data: log_data)
207
169
  end
@@ -209,50 +171,23 @@ module Ciri
209
171
  # transact value from sender to target address
210
172
  def transact(sender:, value:, to:)
211
173
  sender_account = find_account(sender)
212
- to_account = find_account(to)
213
-
214
174
  raise VMError.new("balance not enough") if sender_account.balance < value
215
-
216
- sender_account.balance -= value
217
- to_account.balance += value
218
-
219
- state.set_nonce(sender, sender_account.nonce)
220
- state.set_balance(sender, sender_account.balance)
221
- state.set_balance(to, to_account.balance)
222
- end
223
-
224
- # call instruction
225
- def call_instruction(new_instruction)
226
- origin_instruction = instruction
227
- origin_pc = pc
228
- @instruction = new_instruction
229
- @machine_state.pc = 0
230
-
231
- return_value = yield
232
-
233
- @instruction = origin_instruction
234
- @machine_state.pc = origin_pc
235
- # clear up state
236
- @exception = nil
237
- @output = ''.b
238
- return_value
175
+ state.add_balance(sender, -value)
176
+ state.add_balance(to, value)
239
177
  end
240
178
 
241
179
  # Execute instruction with states
242
180
  # Ξ(σ,g,I,T) ≡ (σ′,μ′ ,A,o)
243
181
  def execute
244
182
  loop do
245
- if (@exception ||= check_exception(@state, machine_state, instruction))
246
- if @burn_gas_on_exception
247
- debug("exception: #{@exception}, burn gas #{machine_state.remain_gas} to zero")
248
- machine_state.consume_gas machine_state.remain_gas
249
- end
250
- return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, EMPTY_SET]
251
- elsif get_op(machine_state.pc) == OP::REVERT
183
+ if exception || set_exception(check_exception(@state, machine_state, instruction))
184
+ debug("check exception: #{exception}")
185
+ return
186
+ elsif get_op(pc) == OP::REVERT
252
187
  o = halt
253
- return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, o]
188
+ return
254
189
  elsif (o = halt) != EMPTY_SET
255
- return [@state, machine_state, sub_state, instruction, o]
190
+ return
256
191
  else
257
192
  operate
258
193
  next
@@ -260,55 +195,60 @@ module Ciri
260
195
  end
261
196
  end
262
197
 
198
+ def with_context(new_context)
199
+ origin_context = execution_context
200
+ @execution_context = new_context
201
+ return_value = yield
202
+ @execution_context = origin_context
203
+ return_value
204
+ end
205
+
206
+ def extend_memory(pos, size)
207
+ machine_state.extend_memory(execution_context, pos, size)
208
+ end
209
+
263
210
  private
264
211
 
265
212
  # O(σ, μ, A, I) ≡ (σ′, μ′, A′, I)
266
213
  def operate
267
- ms = machine_state
268
- w = get_op(ms.pc)
214
+ w = get_op(pc)
269
215
  operation = OP.get(w)
270
216
 
271
- raise "can't find operation #{w}, pc #{ms.pc}" unless operation
217
+ raise "can't find operation #{w}, pc #{pc}" unless operation
272
218
 
273
- op_cost = fork_config.gas_of_operation(self)
274
- old_memory_cost = fork_config.gas_of_memory(ms.memory_item)
275
- ms.consume_gas op_cost
219
+ op_cost, op_refund = fork_schema.gas_of_operation(self)
276
220
 
277
- prev_sub_state = sub_state.dup
221
+ debug("depth: #{depth} pc: #{pc} #{operation.name} gas: #{op_cost} stack: #{stack.size} logs: #{sub_state.log_series.size}")
278
222
 
279
- # call operation
280
- operation.call(self)
281
- # calculate gas_cost
282
- new_memory_cost = fork_config.gas_of_memory(ms.memory_item)
283
- memory_gas_cost = new_memory_cost - old_memory_cost
223
+ consume_gas op_cost
224
+ refund_gas op_refund if op_refund && op_refund > 0
284
225
 
285
- if ms.remain_gas >= memory_gas_cost
286
- ms.consume_gas memory_gas_cost
287
- else
288
- # memory gas_not_enough
289
- @exception = GasNotEnoughError.new "gas not enough: gas remain:#{ms.remain_gas} gas cost: #{memory_gas_cost}"
226
+ # call operation
227
+ begin
228
+ operation.call(self)
229
+ rescue VMError => e
230
+ set_exception(e)
290
231
  end
291
232
 
292
233
  # revert sub_state and return if exception occur
293
234
  if exception
294
- @sub_state = prev_sub_state
235
+ execution_context.revert
295
236
  return
296
237
  end
297
238
 
298
- debug("depth: #{instruction.execute_depth} pc: #{ms.pc} #{operation.name} gas: #{op_cost + memory_gas_cost} stack: #{stack.size} logs: #{sub_state.log_series.size}")
299
- ms.pc = case
300
- when w == OP::JUMP
301
- @jump_to
302
- when w == OP::JUMPI
303
- @jump_to
304
- else
305
- next_valid_instruction_pos(ms.pc, w)
306
- end
239
+ set_pc case
240
+ when w == OP::JUMP
241
+ jump_pc
242
+ when w == OP::JUMPI && jump_pc
243
+ jump_pc
244
+ else
245
+ next_valid_instruction_pos(pc, w)
246
+ end
307
247
  end
308
248
 
309
249
  # determinate halt or not halt
310
250
  def halt
311
- w = get_op(machine_state.pc)
251
+ w = get_op(pc)
312
252
  if w == OP::RETURN || w == OP::REVERT
313
253
  operate
314
254
  output
@@ -324,27 +264,25 @@ module Ciri
324
264
 
325
265
  # check status
326
266
  def check_exception(state, ms, instruction)
327
- w = instruction.get_op(ms.pc)
267
+ w = instruction.get_op(pc)
328
268
  case
329
- when w == OP::INVALID
330
- InvalidOpCodeError.new "can't find op code #{w}"
331
- when OP.input_count(w).nil?
332
- InvalidOpCodeError.new "can't find op code #{w}"
269
+ when w == OP::INVALID || OP.input_count(w).nil?
270
+ InvalidOpCodeError.new "can't find op code 0x#{w.to_s(16)} pc: #{pc}"
333
271
  when ms.stack.size < (consume = OP.input_count(w))
334
272
  StackError.new "stack not enough: stack:#{ms.stack.size} next consume: #{consume}"
335
- when ms.remain_gas < (gas_cost = fork_config.gas_of_operation(self))
336
- GasNotEnoughError.new "gas not enough: gas remain:#{ms.remain_gas} gas cost: #{gas_cost}"
337
- when w == OP::JUMP && instruction.destinations.include?(ms.get_stack(0, Integer))
273
+ when remain_gas < (gas_cost = fork_schema.gas_of_operation(self).yield_self {|gas_cost, _| gas_cost})
274
+ GasNotEnoughError.new "gas not enough: gas remain:#{remain_gas} gas cost: #{gas_cost}"
275
+ when w == OP::JUMP && !destinations.include?(ms.get_stack(0, Integer))
338
276
  InvalidJumpError.new "invalid jump dest #{ms.get_stack(0, Integer)}"
339
- when w == OP::JUMPI && ms.get_stack(1, Integer) != 0 && instruction.destinations.include?(ms.get_stack(0, Integer))
277
+ when w == OP::JUMPI && ms.get_stack(1, Integer) != 0 && !destinations.include?(ms.get_stack(0, Integer))
340
278
  InvalidJumpError.new "invalid condition jump dest #{ms.get_stack(0, Integer)}"
341
279
  when w == OP::RETURNDATACOPY && ms.get_stack(1, Integer) + ms.get_stack(2, Integer) > ms.output.size
342
280
  ReturnError.new "return data copy error"
343
- when stack.size - OP.input_count(w) + OP.output_count(w) > 1024
344
- StackError.new "stack size reach 1024 limit"
281
+ when stack.size - OP.input_count(w) + OP.output_count(w) > stack_size
282
+ StackError.new "stack size reach #{stack_size} limit"
345
283
  # A condition in yellow paper but I can't understand..: (¬Iw ∧W(w,μ))
346
- when instruction.execute_depth > 1024
347
- StackError.new "call depth reach 1024 limit"
284
+ when depth > max_depth
285
+ StackError.new "call depth reach #{max_depth} limit"
348
286
  else
349
287
  nil
350
288
  end