ciri 0.0.2 → 0.0.3

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 (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