ciri 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +8 -3
  3. data/README.md +5 -0
  4. data/Rakefile +24 -2
  5. data/ciri-rlp/.gitignore +11 -0
  6. data/ciri-rlp/.rspec +3 -0
  7. data/ciri-rlp/.travis.yml +5 -0
  8. data/ciri-rlp/CODE_OF_CONDUCT.md +74 -0
  9. data/ciri-rlp/Gemfile +6 -0
  10. data/ciri-rlp/Gemfile.lock +39 -0
  11. data/ciri-rlp/LICENSE.txt +21 -0
  12. data/ciri-rlp/README.md +98 -0
  13. data/ciri-rlp/Rakefile +6 -0
  14. data/ciri-rlp/bin/console +14 -0
  15. data/ciri-rlp/bin/setup +8 -0
  16. data/ciri-rlp/ciri-rlp.gemspec +28 -0
  17. data/{lib → ciri-rlp/lib}/ciri/rlp.rb +2 -2
  18. data/ciri-rlp/lib/ciri/rlp/decode.rb +144 -0
  19. data/ciri-rlp/lib/ciri/rlp/encode.rb +140 -0
  20. data/ciri-rlp/lib/ciri/rlp/serializable.rb +241 -0
  21. data/ciri-rlp/lib/ciri/rlp/version.rb +5 -0
  22. data/ciri-rlp/spec/ciri/fixture_spec.rb +95 -0
  23. data/ciri-rlp/spec/ciri/rlp/decode_spec.rb +68 -0
  24. data/ciri-rlp/spec/ciri/rlp/encode_spec.rb +72 -0
  25. data/ciri-rlp/spec/ciri/rlp/serializable_spec.rb +147 -0
  26. data/ciri-rlp/spec/ciri/rlp_spec.rb +5 -0
  27. data/ciri-rlp/spec/spec_helper.rb +14 -0
  28. data/ciri-utils/.gitignore +11 -0
  29. data/ciri-utils/.rspec +3 -0
  30. data/ciri-utils/.travis.yml +5 -0
  31. data/ciri-utils/CODE_OF_CONDUCT.md +74 -0
  32. data/ciri-utils/Gemfile +6 -0
  33. data/ciri-utils/Gemfile.lock +37 -0
  34. data/ciri-utils/LICENSE.txt +21 -0
  35. data/ciri-utils/README.md +61 -0
  36. data/ciri-utils/Rakefile +6 -0
  37. data/ciri-utils/bin/console +14 -0
  38. data/ciri-utils/bin/setup +8 -0
  39. data/ciri-utils/ciri-utils.gemspec +28 -0
  40. data/{lib → ciri-utils/lib}/ciri/utils.rb +9 -33
  41. data/{lib → ciri-utils/lib}/ciri/utils/logger.rb +0 -0
  42. data/{lib → ciri-utils/lib}/ciri/utils/number.rb +15 -12
  43. data/ciri-utils/lib/ciri/utils/version.rb +5 -0
  44. data/ciri-utils/spec/ciri/utils_spec.rb +5 -0
  45. data/ciri-utils/spec/spec_helper.rb +14 -0
  46. data/ciri.gemspec +6 -3
  47. data/lib/ciri/bloom_filter.rb +83 -0
  48. data/lib/ciri/chain.rb +67 -130
  49. data/lib/ciri/chain/header.rb +2 -2
  50. data/lib/ciri/chain/header_chain.rb +136 -0
  51. data/lib/ciri/chain/transaction.rb +1 -1
  52. data/lib/ciri/crypto.rb +2 -2
  53. data/lib/ciri/db/account_db.rb +145 -0
  54. data/lib/ciri/db/backend/memory.rb +24 -1
  55. data/lib/ciri/devp2p/peer.rb +1 -1
  56. data/lib/ciri/devp2p/rlpx/connection.rb +5 -5
  57. data/lib/ciri/eth/peer.rb +3 -3
  58. data/lib/ciri/eth/protocol_manage.rb +3 -3
  59. data/lib/ciri/eth/protocol_messages.rb +27 -26
  60. data/lib/ciri/ethash.rb +18 -3
  61. data/lib/ciri/evm.rb +101 -56
  62. data/lib/ciri/{utils/lib_c.rb → evm/errors.rb} +28 -18
  63. data/lib/ciri/evm/instruction.rb +3 -1
  64. data/lib/ciri/evm/{serialize.rb → log_entry.rb} +9 -27
  65. data/lib/ciri/evm/machine_state.rb +21 -2
  66. data/lib/ciri/evm/op.rb +19 -16
  67. data/lib/ciri/evm/state.rb +61 -0
  68. data/lib/ciri/evm/vm.rb +78 -102
  69. data/lib/ciri/forks.rb +1 -4
  70. data/lib/ciri/forks/base.rb +55 -0
  71. data/lib/ciri/forks/frontier.rb +37 -10
  72. data/lib/ciri/forks/frontier/cost.rb +186 -0
  73. data/lib/ciri/key.rb +1 -1
  74. data/lib/ciri/pow.rb +1 -1
  75. data/lib/ciri/rlp/decode.rb +6 -3
  76. data/lib/ciri/rlp/encode.rb +10 -10
  77. data/lib/ciri/rlp/serializable.rb +12 -9
  78. data/lib/ciri/serialize.rb +58 -0
  79. data/lib/ciri/trie.rb +362 -0
  80. data/lib/ciri/trie/nibbles.rb +97 -0
  81. data/lib/ciri/trie/nodes.rb +187 -0
  82. data/lib/ciri/{evm → types}/account.rb +20 -13
  83. data/lib/ciri/types/address.rb +16 -11
  84. data/lib/ciri/types/number.rb +73 -0
  85. data/lib/ciri/types/receipt.rb +57 -0
  86. data/lib/ciri/version.rb +1 -1
  87. metadata +82 -19
  88. data/lib/ciri/evm/forks/frontier.rb +0 -183
@@ -21,25 +21,35 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
- require 'ffi'
25
-
26
24
  module Ciri
27
- module Utils
28
-
29
- module LibC
30
- extend FFI::Library
31
- ffi_lib FFI::Library::LIBC
32
-
33
- # memory allocators
34
- attach_function :malloc, [:size_t], :pointer
35
- attach_function :calloc, [:size_t], :pointer
36
- attach_function :valloc, [:size_t], :pointer
37
- attach_function :realloc, [:pointer, :size_t], :pointer
38
- attach_function :free, [:pointer], :void
39
-
40
- # memory movers
41
- attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
42
- attach_function :bcopy, [:pointer, :pointer, :size_t], :void
25
+ class EVM
26
+
27
+ class Error < StandardError
28
+ end
29
+
30
+ class InvalidTransition < Error
31
+ end
32
+
33
+ class InvalidTransaction < Error
34
+ end
35
+
36
+ # VM errors
37
+ class VMError < Error
38
+ end
39
+
40
+ class InvalidOpCodeError < VMError
41
+ end
42
+
43
+ class GasNotEnoughError < VMError
44
+ end
45
+
46
+ class StackError < VMError
47
+ end
48
+
49
+ class InvalidJumpError < VMError
50
+ end
51
+
52
+ class ReturnError < VMError
43
53
  end
44
54
 
45
55
  end
@@ -37,7 +37,9 @@ module Ciri
37
37
  end
38
38
 
39
39
  def get_op(pos)
40
- return OP::INVALID if pos >= (bytes_code || ''.b).size
40
+ code_size = (bytes_code || ''.b).size
41
+ return OP::STOP if pos == code_size
42
+ return OP::INVALID if pos >= code_size
41
43
  bytes_code[pos].ord
42
44
  end
43
45
 
@@ -21,40 +21,22 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
+ require 'ciri/rlp'
25
+ require 'ciri/utils'
24
26
  require 'ciri/types/address'
27
+ require 'ciri/types/number'
25
28
 
26
29
  module Ciri
27
30
  class EVM
28
- module Serialize
29
31
 
30
- extend self
32
+ class LogEntry
33
+ include RLP::Serializable
34
+ schema [{address: Types::Address}, {topics: [Types::Int32]}, :data]
31
35
 
32
- def serialize(item)
33
- case item
34
- when Integer
35
- Utils.big_endian_encode(item)
36
- when Types::Address
37
- item.to_s
38
- else
39
- item
40
- end
36
+ def to_blooms
37
+ [address.to_s, *topics.map {|t| Utils.big_endian_encode(t, size: 32)}]
41
38
  end
42
-
43
- def deserialize(type, item)
44
- if type == Integer && !item.is_a?(Integer)
45
- Utils.big_endian_decode(item.to_s)
46
- elsif type == Types::Address && !item.is_a?(Types::Address)
47
- # check if address represent in Integer
48
- item = Utils.big_endian_encode(item) if item.is_a?(Integer)
49
- Types::Address.new(item.size >= 20 ? item[-20..-1] : ''.b)
50
- elsif type.nil?
51
- # get serialized word
52
- serialize(item).rjust(32, "\x00".b)
53
- else
54
- item
55
- end
56
- end
57
-
58
39
  end
40
+
59
41
  end
60
42
  end
@@ -21,13 +21,32 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
- require_relative 'serialize'
24
+ require 'ciri/serialize'
25
+ require 'ciri/evm/errors'
25
26
 
26
27
  module Ciri
27
28
  class EVM
28
29
 
29
30
  # represent current vm status, include stack, memory..
30
- MachineState = Struct.new(:gas_remain, :pc, :memory, :memory_item, :stack, :output, keyword_init: true) do
31
+ class MachineState
32
+
33
+ attr_reader :remain_gas, :memory, :stack
34
+ attr_accessor :pc, :output, :memory_item
35
+
36
+ def initialize(remain_gas:, pc:, memory:, memory_item:, stack:, output: ''.b)
37
+ raise ArgumentError.new("remain_gas must more than 0") if remain_gas < 0
38
+ @remain_gas = remain_gas
39
+ @pc = pc
40
+ @memory = memory
41
+ @memory_item = memory_item
42
+ @stack = stack
43
+ @output = output
44
+ end
45
+
46
+ def consume_gas(gas)
47
+ raise GasNotEnoughError.new("can't consume gas to negative, remain_gas: #{remain_gas}, consumed: #{gas}") if gas > remain_gas
48
+ @remain_gas -= gas
49
+ end
31
50
 
32
51
  # fetch a list of items from stack
33
52
  def pop_list(count, type = nil)
data/lib/ciri/evm/op.rb CHANGED
@@ -24,7 +24,6 @@
24
24
  require 'ciri/utils'
25
25
  require 'ciri/utils/number'
26
26
  require 'ciri/types/address'
27
- require_relative 'serialize'
28
27
 
29
28
  module Ciri
30
29
  class EVM
@@ -267,7 +266,7 @@ module Ciri
267
266
 
268
267
  def_op :EXTCODESIZE, 0x3b, 0, 1 do |vm|
269
268
  address = vm.pop(Address)
270
- code_size = vm.find_account(address).code&.size || 0
269
+ code_size = vm.get_account_code(address).size
271
270
  vm.push code_size
272
271
  end
273
272
 
@@ -275,8 +274,7 @@ module Ciri
275
274
  address = vm.pop(Address)
276
275
  mem_pos, data_pos, size = vm.pop_list(3, Integer)
277
276
 
278
- account = vm.find_account(address)
279
- code = account.code || ''.b
277
+ code = vm.get_account_code(address)
280
278
  data_end_pos = data_pos + size - 1
281
279
  data = if data_pos >= code.size
282
280
  ''.b
@@ -340,13 +338,13 @@ module Ciri
340
338
  end
341
339
 
342
340
  def_op :SLOAD, 0x54, 1, 1 do |vm|
343
- key = vm.pop
341
+ key = vm.pop(Integer)
344
342
  vm.push vm.fetch(vm.instruction.address, key)
345
343
  end
346
344
 
347
345
  def_op :SSTORE, 0x55, 2, 0 do |vm|
348
- key = vm.pop
349
- value = vm.pop
346
+ key = vm.pop(Integer)
347
+ value = vm.pop(Integer)
350
348
 
351
349
  vm.store(vm.instruction.address, key, value)
352
350
  end
@@ -374,7 +372,7 @@ module Ciri
374
372
  end
375
373
 
376
374
  def_op :GAS, 0x5a, 0, 1 do |vm|
377
- vm.push vm.machine_state.gas_remain
375
+ vm.push vm.machine_state.remain_gas
378
376
  end
379
377
 
380
378
  def_op :JUMPDEST, 0x5b, 0, 0
@@ -421,7 +419,7 @@ module Ciri
421
419
  pos, size = vm.pop_list(2, Integer)
422
420
  log_data = vm.memory_fetch(pos, size)
423
421
  vm.extend_memory(pos, size)
424
- topics = vm.pop_list(i)
422
+ topics = vm.pop_list(i, Integer)
425
423
  vm.add_log_entry(topics, log_data)
426
424
  end
427
425
  end.call(i))
@@ -435,7 +433,7 @@ module Ciri
435
433
  init = vm.memory_fetch(mem_pos, size)
436
434
  vm.extend_memory(mem_pos, size)
437
435
 
438
- contract_address = vm.create_contract(value: value, init: init)
436
+ contract_address, _ = vm.create_contract(value: value, init: init)
439
437
  vm.push contract_address
440
438
  end
441
439
 
@@ -504,7 +502,13 @@ module Ciri
504
502
  end
505
503
 
506
504
  STATICCALL = 0xfa
507
- REVERT = 0xfd
505
+
506
+ # TODO REVERT need to be implement in forks
507
+ def_op :REVERT, -0xfd, 2, 0 do |vm|
508
+ index, size = vm.pop_list(2, Integer)
509
+ vm.output = vm.memory_fetch(index, size)
510
+ vm.extend_memory(index, size)
511
+ end
508
512
 
509
513
  def_op :INVALID, 0xfe, 0, 0 do |vm|
510
514
  raise 'should not invoke INVALID'
@@ -514,7 +518,6 @@ module Ciri
514
518
  refund_address = vm.pop(Address)
515
519
  refund_account = vm.find_account(refund_address)
516
520
 
517
- vm.sub_state.suicide_accounts << vm.instruction.address
518
521
  contract_account = vm.find_account vm.instruction.address
519
522
 
520
523
  if refund_address != vm.instruction.address
@@ -523,12 +526,12 @@ module Ciri
523
526
 
524
527
  contract_account.balance = 0
525
528
 
526
- vm.update_account(refund_account)
527
- vm.update_account(contract_account)
529
+ vm.state.set_balance(refund_address, refund_account.balance)
530
+ vm.state.set_balance(vm.instruction.address, contract_account.balance)
528
531
 
529
532
  # register changed accounts
530
- vm.add_refund_account(refund_account)
531
- vm.add_suicide_account(contract_account)
533
+ vm.add_refund_account(refund_address)
534
+ vm.add_suicide_account(vm.instruction.address)
532
535
  end
533
536
 
534
537
  end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (c) 2018, by Jiang Jinyang. <https://justjjy.com>
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:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
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.
22
+
23
+
24
+ require 'forwardable'
25
+ require 'ciri/db/account_db'
26
+
27
+ module Ciri
28
+ class EVM
29
+ class State
30
+
31
+ extend Forwardable
32
+
33
+ def_delegators :@account_db, :set_nonce, :increment_nonce, :set_balance, :add_balance, :touch_account,
34
+ :find_account, :delete_account, :account_dead?, :store, :fetch, :set_account_code, :get_account_code
35
+
36
+ def initialize(db, state_root: nil)
37
+ @db = db
38
+ @account_db = DB::AccountDB.new(db, root_hash: state_root)
39
+ end
40
+
41
+ def snapshot
42
+ [state_root, @db.dup]
43
+ end
44
+
45
+ def revert(snapshot)
46
+ state_root, db = snapshot
47
+ @db = db
48
+ @account_db = DB::AccountDB.new(db, root_hash: state_root)
49
+ end
50
+
51
+ def commit(snapshot)
52
+ true
53
+ end
54
+
55
+ def state_root
56
+ @account_db.root_hash
57
+ end
58
+
59
+ end
60
+ end
61
+ end
data/lib/ciri/evm/vm.rb CHANGED
@@ -21,11 +21,13 @@
21
21
  # THE SOFTWARE.
22
22
 
23
23
 
24
+ require 'ciri/utils/logger'
25
+ require_relative 'errors'
24
26
  require_relative 'machine_state'
25
27
  require_relative 'instruction'
26
28
  require_relative 'sub_state'
27
29
  require_relative 'block_info'
28
- require_relative 'serialize'
30
+ require_relative 'log_entry'
29
31
 
30
32
  module Ciri
31
33
  class EVM
@@ -40,25 +42,12 @@ module Ciri
40
42
  # other logic of EVM (include transaction logic) in EVM module.
41
43
  class VM
42
44
 
43
- class VMError < StandardError
44
- end
45
- class InvalidOpCodeError < VMError
46
- end
47
- class GasNotEnoughError < VMError
48
- end
49
- class StackError < VMError
50
- end
51
- class InvalidJumpError < VMError
52
- end
53
- class ReturnError < VMError
54
- end
55
-
56
45
  class << self
57
46
  # this method provide a simpler interface to create VM and execute code
58
47
  # VM.spawn(...) == VM.new(...)
59
48
  # @return VM
60
- def spawn(state:, gas_limit:, header: nil, block_info: nil, instruction:, fork_config:)
61
- ms = MachineState.new(gas_remain: gas_limit, pc: 0, stack: [], memory: "\x00".b * 256, memory_item: 0)
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)
62
51
 
63
52
  block_info = block_info || header && BlockInfo.new(
64
53
  coinbase: header.beneficiary,
@@ -84,17 +73,18 @@ module Ciri
84
73
 
85
74
  # helper methods
86
75
  include Utils::Logger
87
- include Serialize
88
76
 
89
- def_delegators :@machine_state, :stack, :pc, :pop, :push, :pop_list, :get_stack,
90
- :memory_item, :memory_item=, :memory_store, :memory_fetch, :extend_memory
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
91
79
  def_delegators :@instruction, :get_op, :get_code, :next_valid_instruction_pos, :get_data, :data, :sender
92
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
93
82
 
94
- attr_reader :machine_state, :instruction, :sub_state, :block_info, :fork_config
83
+ attr_reader :state, :machine_state, :instruction, :sub_state, :block_info, :fork_config
95
84
  attr_accessor :output, :exception
96
85
 
97
- def initialize(state:, machine_state:, sub_state: nil, instruction:, block_info:, fork_config:)
86
+ def initialize(state:, machine_state:, sub_state: nil, instruction:, block_info:,
87
+ fork_config:, burn_gas_on_exception: true)
98
88
  @state = state
99
89
  @machine_state = machine_state
100
90
  @instruction = instruction
@@ -102,31 +92,7 @@ module Ciri
102
92
  @output = nil
103
93
  @block_info = block_info
104
94
  @fork_config = fork_config
105
- end
106
-
107
- # store data to address
108
- def store(address, key, data)
109
- data_is_blank = Ciri::Utils.blank_binary?(data)
110
- # key_is_blank = Ciri::Utils.blank_binary?(key)
111
-
112
- return unless data && !data_is_blank
113
-
114
- # remove unnecessary null byte from key
115
- key = serialize(key).gsub(/\A\0+/, ''.b)
116
- key = "\x00".b if key.empty?
117
-
118
- account = find_account address
119
- account.storage[key] = serialize(data).rjust(32, "\x00".b)
120
- update_account(account)
121
- end
122
-
123
- # fetch data from address
124
- def fetch(address, key)
125
- # remove unnecessary null byte from key
126
- key = serialize(key).gsub(/\A\0+/, ''.b)
127
- key = "\x00".b if key.empty?
128
-
129
- find_account(address).storage[key] || ''.b
95
+ @burn_gas_on_exception = burn_gas_on_exception
130
96
  end
131
97
 
132
98
  # run vm
@@ -138,21 +104,22 @@ module Ciri
138
104
  # low_level create_contract interface
139
105
  # CREATE_CONTRACT op is based on this method
140
106
  def create_contract(value:, init:)
141
- account = find_account(instruction.address)
107
+ caller_address = instruction.address
108
+ account = find_account(caller_address)
142
109
 
143
110
  # return contract address 0 represent execution failed
144
111
  return 0 unless account.balance >= value || instruction.execute_depth > 1024
145
112
 
146
- account.nonce += 1
113
+ state.increment_nonce(caller_address)
114
+ snapshot = state.snapshot
147
115
 
148
116
  # generate contract_address
149
- material = RLP.encode_simple([instruction.address.to_s, account.nonce - 1])
117
+ material = RLP.encode_simple([caller_address.to_s, account.nonce])
150
118
  contract_address = Utils.sha3(material)[-20..-1]
151
119
 
152
120
  # initialize contract account
153
121
  contract_account = find_account(contract_address)
154
- contract_account.nonce = 1
155
- contract_account.code = Utils::BLANK_SHA3
122
+ # contract_account.nonce = 1
156
123
 
157
124
  # execute initialize code
158
125
  create_contract_instruction = instruction.dup
@@ -160,34 +127,48 @@ module Ciri
160
127
  create_contract_instruction.execute_depth += 1
161
128
  create_contract_instruction.address = contract_address
162
129
 
130
+ # TODO refactoring: Maybe should use call_message to execute data
163
131
  call_instruction(create_contract_instruction) do
164
132
  execute
165
133
 
166
- if exception
167
- update_account(Account.new_empty(contract_address))
134
+ deposit_code_gas = fork_config.calculate_deposit_code_gas(output)
135
+
136
+ if deposit_code_gas > remain_gas
137
+ # deposit_code_gas not enough
138
+ contract_address = 0
139
+ elsif exception
140
+ state.touch_account(contract_address)
168
141
  contract_address = 0
142
+ state.revert(snapshot)
169
143
  else
170
144
  # set contract code
171
- contract_account.code = output || ''.b
145
+ set_account_code(contract_address, output)
146
+ # minus deposit_code_fee
147
+ machine_state.consume_gas deposit_code_gas
172
148
  # transact value
173
149
  account.balance -= value
174
150
  contract_account.balance += value
151
+
152
+ state.set_balance(contract_address, contract_account.balance)
153
+ state.set_balance(caller_address, account.balance)
154
+ state.commit(snapshot)
175
155
  end
156
+ [contract_address, exception]
176
157
  end
177
-
178
- # update account
179
- update_account(contract_account)
180
- update_account(account)
181
-
182
- contract_address
183
158
  end
184
159
 
185
160
  # low level call message interface
186
161
  # CALL, CALLCODE, DELEGATECALL ops is base on this method
187
- def call_message(sender:, value:, receipt:, data:, code_address:)
162
+ def call_message(sender:, value:, receipt:, data:, code_address: receipt)
188
163
  # return status code 0 represent execution failed
189
164
  return [0, ''.b] unless value <= find_account(sender).balance && instruction.execute_depth <= 1024
190
165
 
166
+ state.increment_nonce(sender)
167
+
168
+ snapshot = state.snapshot
169
+
170
+ transact(sender: sender, value: value, to: receipt)
171
+
191
172
  message_call_instruction = instruction.dup
192
173
  message_call_instruction.address = receipt
193
174
  message_call_instruction.sender = sender
@@ -196,54 +177,48 @@ module Ciri
196
177
  message_call_instruction.execute_depth += 1
197
178
 
198
179
  message_call_instruction.data = data
199
- message_call_instruction.bytes_code = find_account(code_address).code || ''.b
180
+ message_call_instruction.bytes_code = get_account_code(code_address)
200
181
 
201
- transact(sender: sender, value: value, to: receipt)
202
182
  call_instruction(message_call_instruction) do
203
183
  execute
204
- status = exception.nil? ? 0 : 1
205
- [status, output || ''.b]
184
+
185
+ if exception
186
+ state.revert(snapshot)
187
+ else
188
+ state.commit(snapshot)
189
+ end
190
+
191
+ [status, output || ''.b, exception]
206
192
  end
207
193
  end
208
194
 
195
+ def status
196
+ exception.nil? ? 0 : 1
197
+ end
198
+
209
199
  # jump to pc
210
200
  # only valid if current op code is allowed to modify pc
211
201
  def jump_to(pc)
212
202
  @jump_to = pc
213
203
  end
214
204
 
215
- def account_dead?(address)
216
- Account.account_dead?(@state, address)
217
- end
218
-
219
- def find_account(address)
220
- Account.find_account(@state, address)
221
- end
222
-
223
- # the only method which touch state
224
- # VM do not consider state revert/commit, we let it to state implementation
225
- def update_account(account)
226
- address = account.address.to_s
227
- @state[address] = account
228
- add_touched_account(account)
229
- end
230
-
231
205
  def add_log_entry(topics, log_data)
232
- sub_state.log_series << [instruction.address, topics, log_data]
206
+ sub_state.log_series << LogEntry.new(address: instruction.address, topics: topics, data: log_data)
233
207
  end
234
208
 
235
209
  # transact value from sender to target address
236
210
  def transact(sender:, value:, to:)
237
- sender = find_account(sender)
238
- to = find_account(to)
211
+ sender_account = find_account(sender)
212
+ to_account = find_account(to)
239
213
 
240
- raise VMError.new("balance not enough") if sender.balance < value
214
+ raise VMError.new("balance not enough") if sender_account.balance < value
241
215
 
242
- sender.balance -= value
243
- to.balance += value
216
+ sender_account.balance -= value
217
+ to_account.balance += value
244
218
 
245
- update_account(sender)
246
- update_account(to)
219
+ state.set_nonce(sender, sender_account.nonce)
220
+ state.set_balance(sender, sender_account.balance)
221
+ state.set_balance(to, to_account.balance)
247
222
  end
248
223
 
249
224
  # call instruction
@@ -268,13 +243,14 @@ module Ciri
268
243
  def execute
269
244
  loop do
270
245
  if (@exception ||= check_exception(@state, machine_state, instruction))
271
- debug("exception: #{@exception}")
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
272
250
  return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, EMPTY_SET]
273
251
  elsif get_op(machine_state.pc) == OP::REVERT
274
252
  o = halt
275
- gas_cost = fork_config.cost_of_operation[self]
276
- machine_state.gas_remain -= gas_cost
277
- return [EMPTY_SET, machine_state, sub_state, instruction, o]
253
+ return [EMPTY_SET, machine_state, SubState::EMPTY, instruction, o]
278
254
  elsif (o = halt) != EMPTY_SET
279
255
  return [@state, machine_state, sub_state, instruction, o]
280
256
  else
@@ -294,23 +270,23 @@ module Ciri
294
270
 
295
271
  raise "can't find operation #{w}, pc #{ms.pc}" unless operation
296
272
 
297
- op_cost = fork_config.cost_of_operation[self]
298
- old_memory_cost = fork_config.cost_of_memory[ms.memory_item]
299
- ms.gas_remain -= op_cost
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
300
276
 
301
277
  prev_sub_state = sub_state.dup
302
278
 
303
279
  # call operation
304
280
  operation.call(self)
305
281
  # calculate gas_cost
306
- new_memory_cost = fork_config.cost_of_memory[ms.memory_item]
282
+ new_memory_cost = fork_config.gas_of_memory(ms.memory_item)
307
283
  memory_gas_cost = new_memory_cost - old_memory_cost
308
284
 
309
- if ms.gas_remain >= memory_gas_cost
310
- ms.gas_remain -= memory_gas_cost
285
+ if ms.remain_gas >= memory_gas_cost
286
+ ms.consume_gas memory_gas_cost
311
287
  else
312
288
  # memory gas_not_enough
313
- @exception = GasNotEnoughError.new "gas not enough: gas remain:#{ms.gas_remain} gas cost: #{memory_gas_cost}"
289
+ @exception = GasNotEnoughError.new "gas not enough: gas remain:#{ms.remain_gas} gas cost: #{memory_gas_cost}"
314
290
  end
315
291
 
316
292
  # revert sub_state and return if exception occur
@@ -356,8 +332,8 @@ module Ciri
356
332
  InvalidOpCodeError.new "can't find op code #{w}"
357
333
  when ms.stack.size < (consume = OP.input_count(w))
358
334
  StackError.new "stack not enough: stack:#{ms.stack.size} next consume: #{consume}"
359
- when ms.gas_remain < (gas_cost = fork_config.cost_of_operation[self])
360
- GasNotEnoughError.new "gas not enough: gas remain:#{ms.gas_remain} gas cost: #{gas_cost}"
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}"
361
337
  when w == OP::JUMP && instruction.destinations.include?(ms.get_stack(0, Integer))
362
338
  InvalidJumpError.new "invalid jump dest #{ms.get_stack(0, Integer)}"
363
339
  when w == OP::JUMPI && ms.get_stack(1, Integer) != 0 && instruction.destinations.include?(ms.get_stack(0, Integer))